--- id: DEC-0027 ticket: studio-frame-composer-syscall-and-sprite-alignment title: Studio MUST Adopt `@sdk:composer` for Sprite Composition and Remove `Gfx.set_sprite` status: in_progress created: 2026-04-18 accepted: 2026-04-18 agenda: AGD-0031 plans: - PLN-0053 - PLN-0054 - PLN-0055 tags: - studio - compiler - pbs - stdlib - runtime-alignment - abi - syscall - frame-composer - sprites --- ## Context O `../runtime` já convergiu a ownership pública de frame orchestration para `FrameComposer` e já publicou a ABI pública correspondente no domínio `composer.*`. Para o recorte desta discussão, o ponto normativo relevante é simples: - sprite composition não pertence mais ao domínio público `gfx.*`; - o caminho público legado `gfx.set_sprite` já não representa o owner canônico no runtime; - o Studio ainda expõe esse caminho legado em stdlib, specs, testes, fixtures e exemplos. Isso cria drift entre: - a ABI pública real do runtime; - o contrato que o Studio ensina e emite; - a superfície que PBS usa como owner visível para composição de sprites. O usuário fechou explicitamente o escopo desta decisão: - entra apenas a wave de `@sdk:composer` para sprite composition; - entra `composer.emit_sprite`; - entra remoção total de `Gfx.set_sprite`; - `composer.bind_scene`, `composer.unbind_scene` e `composer.set_camera` ficam para trabalho posterior; - scene bank e concerns de authoring/editor ficam fora deste ticket. ## Decision O Studio MUST adotar `@sdk:composer` como a superfície canônica de stdlib/PBS para sprite composition nesta wave. Mais especificamente: 1. A operação pública de composição de sprites MUST ser exposta por `composer.emit_sprite`. 2. O módulo reservado `@sdk:composer` MUST seguir o mesmo shape editorial hoje usado por `@sdk:gfx`: - um owner host de baixo nível `LowComposer`; - uma façade pública `Composer`. 3. Nesta wave, o contrato de retorno de `composer.emit_sprite` MUST permanecer um `int` cru. 4. `Gfx.set_sprite` MUST ser removido integralmente da superfície normativa e executável do Studio. 5. O repositório MUST NOT manter alias, wrapper de compatibilidade, dual path, ou retargeting silencioso que preserve `Gfx.set_sprite` como API pública. 6. `@sdk:gfx` MUST permanecer restrito às operações primitivas/overlay/backend-adjacent que continuarem pertencendo a esse domínio. 7. `composer.bind_scene`, `composer.unbind_scene` e `composer.set_camera` MUST permanecer fora do escopo de implementação desta decisão e SHALL ser adicionados depois por propagação separada, usando o mesmo padrão editorial. ## Rationale Esta decisão existe para eliminar um dual contract arquitetural que já deixou de ser válido no runtime. Manter `Gfx.set_sprite` como superfície pública teria três efeitos ruins: - preservaria o owner errado no contrato ensinado por PBS/stdlib; - manteria dois modelos mentais concorrentes para a mesma operação; - empurraria a remoção real do legado para um momento futuro mais caro e mais difícil. O recorte sprite-only é deliberado. Ele permite alinhar imediatamente a parte já necessária para o pipeline atual sem misturar: - rollout de scene binding; - rollout de camera control; - ou trabalho de scene bank/editoria de assets. Também ficou decidido que esta wave não tentará resolver modelagem de status com tipos editoriais novos. O retorno cru `int` é suficiente para convergir o boundary agora, sem introduzir uma segunda mudança de API no mesmo passo. ## Technical Specification ### 1. Stdlib Surface O repositório MUST introduzir um módulo reservado `@sdk:composer`. Esse módulo MUST: - declarar `LowComposer` com metadata canônica `Host(module = "composer", name = "emit_sprite", version = 1)`; - declarar a capability canônica correspondente; - expor `Composer.emit_sprite(...)` como façade pública; - usar o mesmo padrão estrutural já adotado por `@sdk:gfx`. O repositório MUST remover de `@sdk:gfx`: - a declaração host `LowGfx.set_sprite(...)`; - a façade pública `Gfx.set_sprite(...)`; - qualquer export relacionado à operação pública antiga. ### 2. Compiler and PBS Propagation As specs e implementações de PBS/compiler MUST passar a tratar `@sdk:composer` como a superfície canônica para sprite composition. Isso inclui: - examples normativos; - exemplos de import; - resolução de stdlib; - fixtures e testes que verificam host metadata; - testes que hoje afirmam `LowGfx`/`Gfx.set_sprite`. O lowering MUST emitir identidade canônica de host do domínio `composer`, não do domínio `gfx`, para sprite emission nesta wave. ### 3. Removal Rule `Gfx.set_sprite` MUST ser tratado como removido, não como deprecated. Consequências obrigatórias: - código de teste antigo que use `Gfx.set_sprite` MUST ser migrado; - fixtures e sample projects SHOULD ser migrados integralmente nesta wave sempre que estiverem dentro do repositório; - docs MUST deixar de ensinar `Gfx.set_sprite` como caminho válido; - a implementação MUST NOT deixar fallback oculto para o caminho antigo. ### 4. Documentation Scope As specs MUST documentar exatamente o que esta wave implementa: - `@sdk:composer` para sprite emission; - remoção de `Gfx.set_sprite`; - permanência de `@sdk:gfx` para primitivas/overlay relevantes. As specs MUST NOT fingir que scene binding ou camera control já fazem parte desta wave no Studio. Elas MAY mencionar que: - `bind_scene`, - `unbind_scene`, - `set_camera` serão adicionados depois seguindo o mesmo padrão, mas sem tratá-los como parte implementada agora. ## Constraints - Esta decisão MUST ser tratada como normativa e locked até revisão explícita do usuário. - Qualquer `plan` derivado dela MUST cobrir integralmente a criação de `@sdk:composer`, a propagação para specs/compiler/tests e a remoção total de `Gfx.set_sprite`. - Nenhum `plan` ou `implement` pode reinterpretar esta decisão como “migrar só internamente” mantendo a API pública antiga. - Nenhum `plan` ou `implement` pode puxar scene bank, bind/unbind de scene ou camera para dentro deste ticket por conveniência. - Se surgir ambiguidade sobre assinatura exata de `emit_sprite`, capability metadata, ou pontos concretos de propagação, o próximo estágio MUST esclarecer isso sem reabrir a remoção total do caminho antigo. ## Revision Log - 2026-04-18: Initial accepted decision from AGD-0031.