--- id: AGD-0023 ticket: studio-play-stop-cartridge-flow title: Play/Stop integration agenda for build/pack freshness-gated runtime preparation status: in_progress created: 2026-04-06 resolved: 2026-04-06 decision: DEC-0020 tags: [studio, play-stop, build, pack, compiler, packer, manifest] --- ## Pain O botão `Play` do Studio hoje só alterna estado visual no shell. Ele ainda não aciona um fluxo operacional real para preparar um conjunto executável de artefatos em `build/`. Isso deixa o shell com uma affordance de execução sem pipeline correspondente e sem um contrato claro entre: - Studio shell, - compiler build, - compiler, - packer, - e a pasta `build/` do projeto. ## Context Domain owner: `studio`. Subdomínios referenciados: `compiler`, `packer`, `runtime`. Já existe um `ShipperWorkspace` no Studio e ele hoje chama `BuilderPipelineService.INSTANCE.build(...)`, mas esse fluxo atual é apenas um scratch inicial. Na prática, hoje ele está compilando bytecode e não deve ser tratado como o contrato definitivo de preparação de runtime do produto. O objetivo desta agenda não é fechar um pipeline de shipping nem materializar um `cartridge/` separado. O escopo desta wave é estritamente o mínimo necessário para o botão `Play` conseguir preparar artefatos rodáveis em `build/`: 1. compilar o PBX necessário; 2. executar o packer necessário; 3. manter ou regenerar os artefatos de `build/` conforme frescor; 4. gerar `manifest.json` em `build/`. Essa agenda trata apenas da preparação dos artefatos em `build/`. A execução `runtime run cartridge` ou equivalente apontando para `build/` fica em agenda separada dentro da mesma discussion. ## Open Questions - [x] O `Play` deve reutilizar exatamente o mesmo pipeline já acionado pelo `ShipperWorkspace`, ou deve haver um service/application layer dedicado para o shell chamar e o workspace apenas consumir? - Resposta proposta: `Play` não deve reaproveitar o scratch atual do `ShipperWorkspace`; ele deve orquestrar chamadas explícitas de `build` e `pack`, cada uma com sua própria política de frescor. - [x] Os artefatos rodáveis desta wave devem viver em `cartridge/` separado ou no próprio `build/`? - Resposta proposta: nesta wave o artifact root runnable deve ser `build/`; não há necessidade de materializar um `cartridge/` separado se o runtime pode consumir o mesmo contrato diretamente de `build/`. - [x] Quem decide se é necessário recompilar ou repackar quando o usuário aperta `Play`? - Resposta proposta: nesta wave, `Play` sempre chama `build` e `pack`; a política fina de drift/freshness fica explicitamente adiada para uma segunda etapa. Quando essa segunda etapa existir, cada domínio será owner de sua própria política de frescor. - [x] Onde deve viver o controle fino de frescor e drift para os artefatos produzidos? - Resposta proposta: quando a wave de drift existir, o controle fino viverá em arquivos dedicados por domínio sob `.studio/`, começando por `asset-workspace.json` e `editor-workspace.json`, separados do snapshot principal de restauração. - [x] O `manifest.json` runtime-facing deve ser derivado do `prometeu.json` do projeto com normalização adicional do Studio, ou copiado quase diretamente com apenas os campos estritamente runtime-facing? - Resposta proposta: o `manifest.json` deve ser sempre gerado de forma determinística em `build/`, a partir do build corrente e do owner metadata do projeto, sem cópia por conveniência. - [x] Como o shell distinguirá falha de build, falha de pack e falha de manifest para refletir estado útil no `Play/Stop`? - Resposta proposta: para o `Play`, basta distinguir `success` e `failed`; logs detalhados das operações devem acompanhar o resultado. `build` falhando bloqueia `pack`, e `manifest` só roda depois do sucesso do build/pack desta wave. ## Options ### Option A - `Play` sempre chama `build` e `pack`, e drift fica para uma wave posterior - **Approach:** Fazer o shell chamar explicitamente `build` e `pack` em toda execução de `Play`; nesta wave não há reuse por drift/freshness, apenas execução determinística com logs e geração final de `manifest.json`. - **Pro:** Destrava a integração do `Play` com o menor contrato correto, sem antecipar um sistema de freshness ainda não normatizado. - **Con:** Pode refazer trabalho desnecessário em execuções sucessivas até a wave de drift existir. - **Maintainability:** Boa, porque mantém o `Play` simples agora e deixa a otimização para uma agenda própria depois. ### Option B - `Play` inspeciona artefatos existentes e decide por conta própria se pode seguir - **Approach:** O shell observa presença ou timestamps de artefatos em `build/` e tenta inferir se basta reutilizar o que já existe. - **Pro:** Menos modelagem inicial de metadata e contratos. - **Con:** Move para o shell uma inteligência que pertence aos domínios de compiler e packer, além de abrir espaço para reuse incorreto sob drift parcial do workspace. - **Maintainability:** Fraca, porque o `Play` vira owner implícito de políticas que não são dele. ## Discussion O ponto central aqui não é apenas "rodar compile + packer". O problema real é separar claramente: - a orquestração do `Play`, - a responsabilidade de `build`, - a responsabilidade de `pack`, - e a futura política de frescor de cada artefato. Se o `Play` inferir por conta própria se precisa rebuildar ou repackar, o shell passa a absorver conhecimento que pertence a outros domínios. Isso cria uma fronteira errada: o shell deixa de ser apenas o caller de operações e passa a ser o árbitro de drift do projeto. Ao mesmo tempo, modelar agora um sistema fino de drift adicionaria complexidade antes da própria integração básica do `Play` existir. Por isso, esta agenda fecha a wave 1 com execução sempre explícita e adia a otimização de freshness para uma agenda própria. O contrato mais limpo é: 1. `Play` pede `build`; 2. `build` executa e retorna `success` ou `failed`, acompanhado de logs; 3. `Play` pede `pack`; 4. `pack` executa e retorna `success` ou `failed`, acompanhado de logs; 5. `manifest.json` é sempre regenerado em `build/` a partir do build corrente e dos metadados do projeto; 6. `Play` só verifica sucesso ou falha dessas operações antes de seguir para a execução. Os constraints já visíveis no repositório reforçam esse recorte: - o compiler atual escreve `build/program.pbx`; - o packer atual escreve `build/assets.pa` e companions de tooling, e o spec do packer afirma explicitamente que cartridge assembly não pertence ao domínio `packer`; - os projetos de teste já mostram `build/` contendo `program.pbx`, `assets.pa` e companions; - `ProjectLocalStudioSetup` já está carregado na `StudioProjectSession`, então o shell já possui o owner correto para coordenar preparation + future run sem precisar deduzir drift por presença oportunista de arquivo. Isso torna a fronteira recomendada mais concreta: 1. `build/` é o artifact root runnable desta wave; 2. compiler e packer continuam donos de seus artefatos dentro de `build/`; 3. `Play` não usa presença de arquivo como regra de validade; 4. drift e freshness ficam explicitamente fora do escopo desta wave e poderão viver em arquivos dedicados por domínio sob `.studio/`; 5. o shell avança apenas quando `build`, `pack` e `manifest` reportarem sucesso. A recomendação inicial desta agenda é: 1. fazer `Play` chamar explicitamente `build` e `pack`; 2. manter `build` e `pack` como operações domain-owned com logs e resultado binário de sucesso/falha nesta wave; 3. adiar o controle fino de drift/freshness para uma agenda própria posterior; 4. promover `build/` como artifact root runnable desta wave; 5. gerar `manifest.json` em `build/` sempre a partir do build corrente; 6. tratar o resultado de cada operação como contrato explícito para o `Play`, sem heurística oportunista baseada apenas em presença de arquivo. ## Resolution Próximo passo sugerido: fechar uma decision de `studio` que trave o fluxo de preparação do `Play/Stop` com estas amarras normativas já explícitas: 1. `Play` é apenas o orquestrador de `build`, `pack` e `manifest`; 2. nesta wave, `build` sempre executa quando chamado pelo `Play`; 3. nesta wave, `pack` sempre executa quando chamado pelo `Play`, e só é chamado se `build` tiver sucesso; 4. o artifact root runnable desta wave é `build/`, não `cartridge/`; 5. `manifest.json` é sempre gerado deterministicamente em `build/`, a partir do build corrente, e não copiado por conveniência; 6. freshness e drift ficam fora do escopo desta wave e, quando tratados, usarão arquivos dedicados por domínio sob `.studio/`, como `asset-workspace.json` e `editor-workspace.json`; 7. `Play` só segue adiante quando as operações necessárias reportarem sucesso.