asset facing PBS addressable

This commit is contained in:
bQUARKz 2026-03-27 11:53:59 +00:00
parent e905055cb0
commit 6150493e95
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8

View File

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