diff --git a/discussion/workflow/agendas/AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md b/discussion/workflow/agendas/AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md index 9bce4809..12b9cbe5 100644 --- a/discussion/workflow/agendas/AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md +++ b/discussion/workflow/agendas/AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md @@ -31,6 +31,8 @@ Os pontos estáveis já conhecidos são: - packer/runtime tratam `asset_id` como identidade estável; - `asset_name` ainda é a superfície mais natural para autoria e tooling; +- o packer já mantém um `PackerRuntimeSnapshot` coerente em memória como projeção operacional do workspace ativo; +- Studio já consome leituras normais a partir desse snapshot operacional em vez de reconstituir a verdade diretamente do filesystem a cada interação; - o pipeline executável hoje já exige disciplina explícita de stack; - a família `19` reforçou a direção de compiler-owned lowering e publication protocol, reduzindo espaço para ambiguidades “aceitas pelo frontend, rejeitadas mais tarde”. @@ -39,14 +41,190 @@ Isso sugere um princípio comum: - a linguagem pode continuar amigável na superfície, - mas o compiler precisa assumir explicitamente o trabalho de normalizar essa superfície para um contrato executável estável. +O novo ponto importante é que, para referências a asset, essa “superfície amigável” não precisa nascer de nomes soltos escritos manualmente nem de arquivos tipados gerados sem owner claro. + +Ela pode ser tratada como uma projeção simbólica derivada da autoridade operacional já existente no packer: + +- `PackerRuntimeSnapshot` como fonte operacional coerente do conjunto de assets visíveis; +- Studio como host capaz de sintetizar uma superfície tipada a partir desse snapshot; +- PBS como consumidor dessa superfície simbólica; +- compiler como responsável por lowerar essa superfície para identidade estável de runtime. + +O entendimento atual desta discussão mudou um ponto importante do modelo anterior: + +- `asset_name` não parece justificar existência como campo operacional separado; +- o dado estrutural realmente útil já está no root do asset dentro de `assets/`; +- portanto a superfície simbólica candidata pode ser o próprio address normatizado derivado do diretório, e não um nome livre fornecido pelo autor. + +Exemplo de direção: + +- asset root em disco: `assets/some/dir/maluco/asset/asset.json` +- superfície simbólica projetada para código/tooling: `assets.some.dir.maluco.asset` + +Na direção atual, essa superfície não deve ser tratada como enum flat simples. + +O modelo preferido é: + +- `assets` como árvore hierárquica sintetizada de compile; +- nós intermediários como namespaces de compile; +- apenas folhas correspondentes a asset roots reais como valores terminais do tipo `Addressable`. + +Exemplo: + +- `assets.ui` pode ser apenas namespace; +- `assets.ui.borders` pode ser apenas namespace; +- `assets.ui.borders.panel` pode ser uma folha `Addressable`. + +Isso implica uma regra estrutural importante: + +- um asset terminal não pode ocupar um prefixo que também precise funcionar como namespace para outros assets. + +Exemplo inválido: + +- existir asset terminal em `assets/ui` +- e também existir asset terminal em `assets/ui/borders/panel` + +porque `assets.ui` não pode ser simultaneamente `Addressable` terminal e namespace. + +Operational note: + +- Studio should prevent creation of a new asset root when that path would collide with an existing asset terminal/namespace boundary. +- In practice, if an asset already exists at a path, Studio should not allow creating descendant assets under that same terminal path. +- Likewise, if descendants already exist, Studio should not allow creating a new asset at their prefix path. +- This must become an explicit Studio-side creation and move constraint, not just a compiler-side validation concern. + +Nesse modelo: + +- Studio mostra o address normatizado do asset, não um `asset_name` livre; +- packer não precisa mais ler nem escrever `asset_name` como variável operacional em `asset.json`; +- packer apenas reconhece, normaliza e publica o address derivado do root do asset sem persistir esse address como estado redundante em disco; +- `asset_id` permanece como primary key operacional estável do asset dentro do packer/runtime; +- o `address` passa a ser a identidade simbólica usada pelos compiladores para referenciar o asset em superfícies de autoria/compile; +- renomear ou mover diretório muda o address e quebra referências em compile time; +- tooling de rename/move pode existir para amortecer esse custo, mas a quebra deixa de ser ambígua e passa a ser explícita. + +Com esse contexto, o coração real da discussão de asset references não é mais “nome versus `asset_id`”. + +O ponto central passa a ser: + +- como PBS/SDK transforma um `address` autoral em `asset_id` operacional. + +Exemplo de shape ainda ilustrativo: + +```pbs +fn load(addressable: Addressable, bankRef: BankRef) -> LoadError +``` + +A dúvida principal não é o nome exato da API, mas o lugar da resolução: + +- a função do SDK recebe um símbolo/valor addressable e resolve internamente para `asset_id`; +- ou o próprio `Addressable` já é uma declaração/superfície sintetizada a partir do modelo vindo do backend, carregando a resolução necessária antes mesmo de entrar na função; +- ou ainda existe uma terceira forma híbrida em que a superfície autoral é simbólica, mas o lowering do compiler reescreve a chamada para a variante já normalizada em `asset_id`. + +Essa decisão é o núcleo arquitetural da agenda porque define: + +- o quanto a API pública do SDK continua “asset-address-first”; +- o quanto o compiler participa do lowering antes da chamada; +- e qual parte do sistema é owner da ponte entre identidade simbólica de compile e identidade operacional de runtime. + +O modelo que hoje parece mais coerente com PBS não é colocar essa resolução no corpo de um `declare service`. + +O desenho mais alinhado com a linguagem atual é: + +- `declare service` público continua sendo wrapper ergonômico normal; +- a metadata reservada de lowering fica no `declare host`, junto do próprio contrato host-backed; +- o compiler consome essa metadata ao baixar a chamada host-backed; +- o parâmetro marcado como asset-facing é resolvido de `Addressable` para `asset_id` durante o lowering. + +Exemplo ilustrativo: + +```pbs +declare host low_assets { + [Host(module = "assets", name = "preload", version = 1)] + [AssetLowering(param = 0)] + fn preload(addressable: Addressable, bank_id: int) -> LoadError; +} + +declare service Assets { + fn load(addressable: Addressable, bank: BankRef) -> LoadError { + return low_assets.preload(addressable, bank.id()); + } +} +``` + +Neste desenho: + +- o `service` não contém lógica especial de resolução; +- ele só encaminha a chamada de forma autoral/ergonômica; +- a assinatura host-backed explicita qual parâmetro sofre asset-lowering; +- o compiler sabe exatamente onde injetar o `asset_id` porque a própria assinatura `declare host` carrega o contrato; +- o lowering ocorre antes da forma final host/syscall, sem exigir placeholder artificial no corpo do `service`. + +Outro limite arquitetural precisa ficar explícito: + +- `asset` não é a superfície correta para endereçamento de recursos internos; +- o `asset` entra como unidade de instalação/publicação/carregamento; +- o acesso a internos só faz sentido depois que o conteúdo foi instalado em um `bank` de runtime; +- portanto, members internos pertencem à superfície do `bank` e não à superfície do `asset`. + +Isso impede uma confusão comum: + +- `assets.foo.bar` parece sugerir que `bar` é membro estável do asset em si; +- mas, neste modelo, `bar` só deve existir como símbolo depois que um `bank` específico expõe esse espaço de endereçamento. + +Como diferentes bancos têm semânticas distintas (`tile bank`, `sound bank`, e futuros bancos), o endereçamento de internos é family-specific e provavelmente pertence a uma spec transversal de runtime/banks, não à política básica de asset references. + +Também surge uma separação importante entre pipeline de compile e runtime packer: + +- o frontend PBS não deveria consultar o packer diretamente para descobrir a superfície de assets; +- a estrutura simbólica de assets deve chegar ao compile como dado já preparado na borda backend/orquestradora; +- isso significa que o backend de compile precisa transportar para o frontend PBS uma projeção dos assets visíveis; +- essa mesma porta abre precedente para outras estruturas futuras entrarem no frontend via request de compile, em vez de acoplá-lo a serviços externos em tempo de frontend. + +For the current agenda scope, the minimum compile projection is intentionally small: + +- `address` +- `asset_id` + +No richer asset payload is required yet for this policy discussion. + +Outro recorte importante: + +- preload já existe e não é o objeto principal desta agenda; +- ele pode ser citado como contexto de integração; +- mas a discussão aqui não é “como preload funciona”; +- a discussão é “como asset references em PBS/SDK se resolvem até o ponto em que o caminho low-level já opera com `asset_id`”. + ## Open Questions - [ ] Referências a asset devem permanecer nomeadas na superfície PBS, mesmo se o lowering resolver parte delas para identidade estável? +- [ ] A superfície simbólica de asset em PBS deve ser derivada explicitamente do `PackerRuntimeSnapshot` mantido pelo packer para o projeto ativo? +- [ ] O enum ou namespace sintético derivado de assets pertence ao Studio como host/tooling, ao packer como serviço, ou a uma fronteira compartilhada formalizada entre ambos? +- [ ] `asset_name` deve ser removido como variável operacional e substituído pelo address normatizado derivado do root do asset? +- [ ] Studio deve adotar esse address como identidade primária visível ao usuário em vez de exibir um nome livre fornecido pelo autor? +- [ ] O address simbólico canônico deve ser derivado do path relativo em `assets/` segmentado como namespace (`assets.foo.bar.baz`)? +- [ ] A superfície `assets...` deve ser normatizada como árvore hierárquica com namespaces intermediários e folhas terminais `Addressable`, em vez de enum flat? +- [ ] A plataforma deve proibir colisões onde um asset terminal também precisaria atuar como namespace-prefixo de outros assets? +- [ ] A projeção de assets consumida pelo frontend PBS deve entrar pelo backend no request de compile, em vez de o frontend consultar diretamente o packer? +- [ ] Quais outras estruturas além de assets podem, no futuro, seguir esse mesmo padrão de entrada BE -> FE no compile? +- [ ] A resolução de `address` para `asset_id` acontece dentro da função pública do SDK, no tipo/símbolo sintetizado (`Addressable` ou equivalente), ou no lowering do compiler antes da chamada? +- [ ] Se existir um tipo público como `Addressable`, ele representa só a superfície simbólica autoral ou já embute a identidade operacional necessária para a chamada low-level? +- [ ] A API pública do SDK deve permanecer centrada em argumentos simbólicos (`address`, `Addressable`) mesmo quando o backend final consome `asset_id`? +- [ ] O compiler deve reescrever chamadas de SDK asset-facing para formas normalizadas por `asset_id`, mantendo a superfície autoral intacta? +- [ ] Como separar, normativamente, a superfície de referência ao `asset` da futura superfície de endereçamento dos members expostos por um `bank` após instalação? +- [ ] Esse contrato de members expostos por `bank` deve viver em spec transversal (`vm-arch`/runtime surface) em vez de ficar embutido na política de asset references do PBS? - [ ] O descarte de retorno ignorado deve ser uma regra geral de `expression statement`, ou uma exceção localizada para certos callsites? - [ ] Até onde PBS quer empurrar “surface ergonomics, compiler normalization” como princípio geral para APIs de jogo? - [ ] Quais casos precisam continuar dinâmicos em runtime e portanto não podem ser lowered agressivamente em compile time? - [ ] Devemos publicar warnings opcionais para retorno ignorado ou rename-fragile asset references, ou isso fica fora do escopo inicial? +Open questions still considered active after the current discussion: + +- [ ] Qual é o shape mínimo e normativo do modelo de assets entregue ao compile/LSP além de `address` e `asset_id`, se algum enriquecimento futuro se mostrar necessário? +- [ ] Quais outras estruturas além de assets podem, no futuro, seguir esse mesmo padrão de entrada BE -> FE no compile? +- [ ] O descarte de retorno ignorado deve virar warning geral de `expression statement` com valor, ou warning apenas para superfícies específicas? +- [ ] Quais casos, além de disponibilidade em runtime, realmente precisam permanecer dinâmicos e fora da resolução estática baseada no snapshot? + ## Options ### Option A - Keep Surface Simple, Keep Runtime Rules Visible @@ -82,13 +260,113 @@ O ponto central desta discussão é definir o limite dessa política: - quando PBS só normaliza lowering, - e quando precisa criar uma abstração nova de linguagem. +Com o contexto atual do packer, a pergunta fica mais precisa: + +- a autoria PBS deve continuar apontando para identificadores “humanos” sem owner operacional claro; +- ou deve consumir uma superfície simbólica derivada do snapshot coerente que o packer já mantém para o projeto? + +Se a segunda direção for adotada, o enum sintético de assets não é só ergonomia de editor. +Ele passa a ser a projeção tipada, em domínio PBS, de uma autoridade operacional já existente no packer. + +Isso desloca a discussão de “nome versus `asset_id`” para uma formulação melhor: + +- qual é a superfície simbólica correta para o jogo autorar; +- quem é owner dessa superfície; +- e como essa superfície é lowered para a identidade estável final. + +Também desloca uma segunda discussão para fora do escopo imediato: + +- a agenda atual pode fechar como o jogo referencia o `asset` enquanto unidade instalável/publicável; +- mas não deve colapsar isso com o addressing dos internos expostos por `banks`. + +O modelo mais correto parece ser: + +1. o jogo referencia um asset por uma superfície simbólica derivada da autoridade operacional do packer; +2. esse asset é instalado/publicado em um `bank` de runtime; +3. somente o `bank` passa a expor o espaço de endereçamento de recursos internos; +4. esse espaço de endereçamento é específico da família do banco e merece contrato próprio. + +Ao mesmo tempo, a discussão de root references passa a ter três frentes explícitas: + +1. `Studio` + - como o asset é exibido e selecionado na UI; + - se o address derivado do root substitui o `asset_name` como identidade visível; + - como impedir criação/move que faça um path atuar ao mesmo tempo como asset terminal e namespace; +2. `packer` + - como o snapshot operacional publica o address normatizado; + - se `asset.json` deixa de carregar `asset_name` como dado relevante; + - como moves/renames impactam o address e a invalidade de referências; +3. `compiler/pbs` + - como a projeção de assets entra no compile; + - como o frontend PBS consome essa projeção sem consultar o packer diretamente; + - como o lowering converte `assets.foo.bar` para identidade estável de runtime; + - em que ponto da superfície SDK/PBS a ponte `address -> asset_id` realmente acontece. + +No momento, a direção técnica mais forte para essa ponte é: + +- `Addressable` permanece como superfície simbólica de autoria; +- `declare host` asset-backed recebe metadata reservada como `[AssetLowering(param = N)]`; +- o compiler resolve esse parâmetro durante o lowering da chamada host-backed; +- `declare service` público permanece apenas como wrapper ergonômico sobre esse contrato. + ## Resolution Direção recomendada por enquanto: 1. tratar os dois temas como uma única discussão de política de surface-versus-lowering em PBS; 2. preferir a direção **Option B** como baseline; -3. fechar depois duas decisões derivadas: - - política de asset references para código de jogo; +3. tratar a política de asset references como discussão entre três camadas explícitas: + - `PackerRuntimeSnapshot` como autoridade operacional do conjunto de assets; + - superfície simbólica sintetizada para consumo em PBS; + - lowering compiler-owned para identidade estável de runtime; +4. explorar explicitamente a remoção de `asset_name` como variável operacional em favor do address derivado do root relativo em `assets/`; +5. tratar como requisito arquitetural que a projeção de assets chegue ao frontend PBS pelo backend/orquestração do compile, não por consulta direta do frontend ao packer; +6. tratar como questão central da agenda o ponto exato em que a superfície PBS/SDK converte `address` em `asset_id`; +7. fechar depois duas decisões derivadas: + - política de asset references para código de jogo, agora incluindo owner, forma do address canônico e contrato de entrada BE -> FE da projeção de assets; - política de descarte de retorno ignorado em `expression statement`; -4. evitar introduzir nova abstração de linguagem antes de esgotar a via de normalização no compiler. +8. registrar explicitamente que addressing de recursos internos instalados em `banks` não pertence ao contrato básico de `asset references` e deve seguir discussão/spec própria, provavelmente em superfície transversal de runtime/banks; +9. evitar introduzir nova abstração de linguagem antes de esgotar a via de normalização no compiler e a via de superfície simbólica derivada da autoridade operacional já existente no packer. + +### Current preferred technical shape + +Sem fechar ainda a decision final, a forma técnica hoje preferida nesta agenda é: + +1. `declare host` asset-backed carrega `[AssetLowering(param = ...)]`; +2. o parâmetro marcado usa `Addressable` como surface autoral; +3. `declare service` público chama normalmente esse host wrapper; +4. o compiler resolve `address -> asset_id` ao baixar a chamada host-backed; +5. o runtime final continua vendo apenas o contrato operacional por `asset_id`. + +Related shape for the synthetic asset surface: + +1. `assets` is a hierarchical compile-time tree, not a flat enum; +2. intermediate nodes are namespaces only; +3. terminal asset leaves are typed as `Addressable`; +4. compile-time projection currently needs only `address` and `asset_id`; +5. Studio/runtime-tooling must reject terminal/namespace path collisions; +6. Studio should not allow creating or moving assets in ways that would make the same path act as both terminal asset and namespace prefix. + +Example: + +```pbs +declare host low_assets { + [Host(module = "assets", name = "preload", version = 1)] + [AssetLowering(param = 0)] + fn preload(addressable: Addressable, bank_id: int) -> LoadError; +} + +declare service Assets { + fn load(addressable: Addressable, bank: BankRef) -> LoadError { + return low_assets.preload(addressable, bank.id()); + } +} +``` + +In this example: + +- the author-facing API remains `Assets.load(addressable, bank)`; +- `low_assets.preload(...)` is the host-backed contract surface; +- `[AssetLowering(param = 0)]` tells the compiler that parameter `0` must be resolved from symbolic `Addressable` to operational `asset_id`; +- the service body remains ordinary PBS code; +- the special behavior lives in reserved lowering metadata on the `declare host` signature.