From bd0b1924c29ef5c8e76f313b972636a7dedf9ff7 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 27 Mar 2026 16:13:20 +0000 Subject: [PATCH] implement PBS low-level asset host surface --- discussion/index.ndjson | 3 +- ...008-pbs-low-level-asset-manager-surface.md | 196 ++++++++++++++++++ ...004-pbs-low-level-asset-manager-surface.md | 176 ++++++++++++++++ ...004-pbs-low-level-asset-manager-surface.md | 194 +++++++++++++++++ ...ing and Loader Resolution Specification.md | 33 +++ ... and Runtime Capabilities Specification.md | 5 + ...ent Packaging and Loading Specification.md | 22 ++ .../resources/stdlib/1/sdk/asset/main.pbs | 17 ++ .../resources/stdlib/1/sdk/asset/mod.barrel | 1 + .../compiler/pbs/PbsFrontendCompilerTest.java | 38 ++++ .../PbsGateUSdkInterfaceConformanceTest.java | 48 ++++- .../PbsInterfaceModuleSemanticsTest.java | 20 ++ .../pbs/stdlib/InterfaceModuleLoaderTest.java | 35 ++++ .../services/PBSFrontendPhaseServiceTest.java | 55 +++++ 14 files changed, 841 insertions(+), 2 deletions(-) create mode 100644 discussion/workflow/agendas/AGD-0008-pbs-low-level-asset-manager-surface.md create mode 100644 discussion/workflow/decisions/DEC-0004-pbs-low-level-asset-manager-surface.md create mode 100644 discussion/workflow/plans/PLN-0004-pbs-low-level-asset-manager-surface.md create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/mod.barrel diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 68e20867..6c284e9f 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":8,"AGD":8,"DEC":4,"PLN":4,"LSN":23,"CLSN":1}} +{"type":"meta","next_id":{"DSC":9,"AGD":9,"DEC":5,"PLN":5,"LSN":23,"CLSN":1}} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-palette-management-in-studio.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]} @@ -6,3 +6,4 @@ {"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0006","status":"open","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[{"id":"AGD-0006","file":"AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} +{"type":"discussion","id":"DSC-0008","status":"open","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-pbs-low-level-asset-manager-surface.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[{"id":"DEC-0004","file":"DEC-0004-pbs-low-level-asset-manager-surface.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0004","file":"PLN-0004-pbs-low-level-asset-manager-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0004"]}],"lessons":[]} diff --git a/discussion/workflow/agendas/AGD-0008-pbs-low-level-asset-manager-surface.md b/discussion/workflow/agendas/AGD-0008-pbs-low-level-asset-manager-surface.md new file mode 100644 index 00000000..9ffc3ed4 --- /dev/null +++ b/discussion/workflow/agendas/AGD-0008-pbs-low-level-asset-manager-surface.md @@ -0,0 +1,196 @@ +--- +id: AGD-0008 +ticket: pbs-low-level-asset-manager-surface +title: PBS Low-Level Asset Manager Surface for Runtime AssetManager +status: accepted +created: 2026-03-27 +resolved: 2026-03-27 +decision: DEC-0004 +tags: [compiler, pbs, runtime, asset-manager, host-abi, stdlib, asset] +--- + +## Pain + +PBS ainda nao tem uma superficie low-level explicita para operar o `AssetManager` do `../runtime`, apesar de ja existir um contrato host-backed claro para outras areas como `log` e `gfx`, e uma superficie VM-owned consolidada para `input`. + +Hoje isso deixa uma lacuna pratica: + +1. o runtime ja publica a familia de syscalls `asset.load`, `asset.status`, `asset.commit` e `asset.cancel`; +2. o `AssetBridge`/`AssetManager` ja carregam semantica real de slots, handles, lifecycle e status-first; +3. o SDK PBS ainda nao declara o owner low-level equivalente; +4. a agenda `AGD-0006` discute a superficie simbolica author-facing e o lowering de `Addressable` para identidade estavel, mas nao fecha o shape minimo do contrato host-backed bruto que deve existir por baixo. + +Sem essa camada low-level bem definida, o proximo passo fica mais dificil: + +- nao existe base clara para um futuro `declare service Assets` ergonomico; +- nao existe alvo normativo preciso para o frontend PBS validar e lowerar; +- mesmo com o runtime agora convergido para `asset_id`, PBS ainda precisa decidir qual shape low-level publicar no SDK e como nomear/encapsular esse contrato. + +## Context + +Domain owner: + +- `compiler/pbs` + +Referenced domains: + +- `runtime` +- possivelmente `vm-arch` se a discussao tocar contrato transversal de handles/status layout + +O runtime ja fornece evidencia concreta para esta discussao: + +- `../runtime/crates/console/prometeu-hal/src/syscalls/domains/asset.rs` publica `module = "asset"` com `load`, `status`, `commit` e `cancel`; +- `../runtime/docs/specs/runtime/15-asset-management.md` fixa o MVP syscall shape como: + - `asset.load(asset_id, slot) -> (status:int, handle:int)` + - `asset.status(handle) -> status:int` + - `asset.commit(handle) -> status:int` + - `asset.cancel(handle) -> status:int` +- `../runtime/crates/console/prometeu-hal/src/asset_bridge.rs` agora expõe `load(&self, asset_id: AssetId, slot_index: usize) -> Result`; +- `../runtime/docs/specs/runtime/16-host-abi-and-syscalls.md` reforca que o caller nao fornece `asset_name` nem `bank_type`; +- `../runtime/docs/vm-arch/ISA_CORE.md` e `../runtime/docs/vm-arch/ARCHITECTURE.md` ja tratam o stack contract canonico como `asset_id, slot -> status, handle`; +- o mesmo runtime spec usa `asset_id` como chave normativa para preload. + +No workspace atual, isso elimina a ambiguidade antiga do lado runtime: + +1. o runtime low-level ja convergiu para `asset_id + slot`; +2. preload/bootstrap e `asset_table` usam a mesma identidade; +3. a tensao restante agora esta quase toda do lado PBS, que ainda precisa conectar essa ABI crua com a futura superficie simbolica discutida em `AGD-0006`. + +Os exemplos existentes no SDK PBS ajudam a enquadrar a forma esperada: + +- `log` usa `declare host LowLog` como owner bruto e `declare service Log` como fachada ergonomica; +- `gfx` usa `declare host LowGfx` e `declare service Gfx` no mesmo padrao; +- `input` nao usa host ABI, porque ja foi modelado como builtin VM-owned, entao ele serve mais como contraexemplo util do que como template direto. + +O que esta discussion precisa responder nao e "como o jogo vai referenciar assets de forma simpatica". +Essa parte ja esta sendo discutida em outra agenda. + +O foco aqui e mais baixo nivel: + +- qual contrato PBS deve declarar para falar com a superficie `asset` que o runtime ja resolve por host ABI; +- quais tipos/status/handles/slots precisam existir no SDK/interface module; +- onde a fronteira entre espelho fiel do runtime atual e normalizacao futura deve ser colocada. + +Clarification: + +- despite the user shorthand "assets.load", the concrete runtime surface checked on 2026-03-27 in `../runtime` is still `module = "asset"` and `name = "load"`; +- the substantive update is the argument contract, now `asset_id, slot`, not `asset_name, kind, slot`. +- the canonical capability name checked on 2026-03-27 is also `asset` in the singular: + - runtime caps publish `ASSET`; + - runtime cartridge capability mapping uses `"asset" -> ASSET`; + - PBS capability spelling examples follow singular names such as `"gfx"` and should stay aligned with the runtime capability registry. + +## Open Questions + +- [ ] Qual deve ser o owner low-level em PBS: `LowAsset`, `LowAssets`, `LowAssetManager`, ou outro naming alinhado ao padrao existente? +- [ ] A primeira versao do contrato PBS deve espelhar exatamente o runtime atual (`asset.load(asset_id, slot)`), ou ainda vale esconder parte disso atras de wrappers nominais mais fortes? +- [ ] Devemos modelar a entrada de `load` em PBS low-level como `int asset_id` cru, como um nominal type dedicado (`AssetId`), ou como um builtin reservado com semantica de runtime id? +- [ ] O baixo nivel deve assumir apenas o ABI do runtime publicado hoje, deixando qualquer lowering de `Addressable` para uma camada superior, ou deve nascer ja preparado para metadata reservada de asset lowering? +- [ ] Como representar `slot` na interface PBS: `int` cru, nominal `SlotIndex`, ou algum wrapper pequeno que continue baixando para o mesmo slot integer da ABI? +- [ ] Devemos refletir normativamente que `bank kind` nao e mais argumento publico de `asset.load`, porque o runtime o deriva de `asset_table`? +- [ ] `handle` deve ser um `int` cru no MVP, ou um builtin/nominal type dedicado para evitar mistura acidental com outros ids? +- [ ] Os status de `load`, `status`, `commit` e `cancel` devem ficar como `int` cru no low-level, ou o SDK PBS deve declarar enums dedicados desde o inicio? +- [ ] `bank_info` e `slot_info`, que existem no runtime como modulo `bank`, fazem parte da mesma discussion ou devem ficar fora do primeiro recorte do asset low-level? +- [ ] O contrato PBS low-level deve cobrir somente `load/status/commit/cancel`, ou tambem preparar desde ja preload/bootstrap observability e telemetry adjacentes? +- [ ] A surface low-level deve ser totalmente host-backed via `declare host`, com um `declare service Assets` opcional depois, seguindo `log/gfx`? +- [ ] A agenda deve assumir como principio que PBS nunca fala diretamente com `AssetManager`, e sim apenas com a ABI `module/name/version` publicada pelo runtime? +- [ ] Qual parte desta decisao precisa propagar para specs PBS (`6.2`, stdlib surface, talvez runtime capabilities) e qual parte fica apenas como implementacao de SDK/resources? + +## Options + +### Option A - Mirror The Current Runtime ABI Exactly +- **Approach:** declarar em PBS um `declare host` que reflita exatamente `asset.load(asset_id, slot)`, `asset.status(handle)`, `asset.commit(handle)` e `asset.cancel(handle)`, usando `int` cru em todos os pontos onde a ABI hoje e inteira. +- **Pro:** reduz risco de desalinhamento com o runtime atual e facilita implementacao incremental rapida. +- **Con:** empurra pouca semantica para o SDK e deixa PBS exposto a mistura acidental entre `asset_id`, `slot` e `handle`. +- **Maintainability:** media. O alinhamento ABI e forte, mas a superficie editorial continua pobre. + +### Option B - Publish A Low-Level PBS Surface That Is ABI-Faithful But Type-Normalized +- **Approach:** manter alinhamento estrito com `module = "asset"` e com os nomes/version atuais, mas permitir que PBS exponha wrappers nominais/enums/structs para `asset_id`, `handle`, `slot` e status, desde que o lowering final preserve o ABI publicado. +- **Pro:** segue o padrao de `log/gfx`, melhora legibilidade do SDK e deixa o compiler com um alvo mais seguro sem esconder que a ownership continua no runtime ABI. +- **Con:** exige decidir cedo quais normalizacoes de tipo sao so editoriais e quais ja implicam semantica nova. +- **Maintainability:** alta. A ABI continua estavel, mas a superficie PBS nao precisa ficar presa a `int` solto para sempre. + +### Option C - Skip The Raw Surface And Design Only The Author-Facing Assets API +- **Approach:** postergar a definicao low-level e discutir apenas a futura fachada `Assets` baseada em `Addressable`/`asset_id`, deixando o contrato host-backed implicito por enquanto. +- **Pro:** conversa mais proxima da experiencia do autor de jogo. +- **Con:** deixa o pipeline sem camada de base; mistura decisao ergonomica com decisao de ABI; repete exatamente o tipo de ambiguidade que `log/gfx` evitaram ao separar owner bruto de fachada. +- **Maintainability:** baixa. A implementacao tendera a inventar detalhes sem uma superficie canonica intermediaria. + +## Discussion + +No estado atual, a direcao mais coerente parece ser a Option B. + +Motivos: + +1. `log` e `gfx` ja mostraram um padrao util: owner low-level explicito via `declare host`, opcionalmente embrulhado por `declare service`; +2. `input` reforca que nem toda superficie do SDK precisa ser host-backed, entao esta agenda deve ser cuidadosa para nao confundir "API de jogo" com "contrato ABI"; +3. a agenda `AGD-0006` precisa de uma base concreta onde o lowering author-facing possa cair; +4. o runtime ja tem ABI publicada para `asset`, entao PBS nao deve inventar uma forma paralela nem falar com `AssetManager` de forma privilegiada; +5. o runtime recente removeu a maior parte da ambiguidade de carga ao convergir para `asset_id + slot`, entao a agenda PBS pode ficar mais assertiva e menos especulativa; +6. ainda assim, espelhar apenas `int/int/int` sem nenhuma disciplina editorial empurra custo cognitivo desnecessario para o SDK e para os testes do compiler. + +O principal cuidado e separar dois eixos: + +- fidelidade ABI: + - `module`, `name`, `version`, aridade e retorno precisam permanecer alinhados ao runtime; +- editorial/type shape em PBS: + - wrappers nominais, enums ou pequenos value types podem ser aceitaveis se o lowering terminar no mesmo contrato host-backed. + +The recent runtime change also sharpens the recommendation: + +- this agenda no longer needs to entertain a name-based low-level path as a first-class candidate; +- if PBS wants `Addressable` or symbolic authoring, that should lower into `asset_id` before the final host call; +- the low-level owner should be designed around the runtime contract that actually exists now, not around the retired `asset_name` surface. + +Current convergence already accepted in discussion: + +1. the low-level PBS owner name should be `LowAssets`; +2. the first PBS low-level surface should keep `int` raw for `asset_id` and `slot`; +3. the declaration shape should be `declare host LowAssets { ... }`; +4. capability spelling should remain `asset`, not `assets`, to preserve alignment with the runtime capability and syscall registries even if the PBS owner type is pluralized. + +Illustrative direction: + +```pbs +declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + + [Host(module = "asset", name = "commit", version = 1)] + [Capability(name = "asset")] + fn commit(loading_handle: int) -> int; + + [Host(module = "asset", name = "cancel", version = 1)] + [Capability(name = "asset")] + fn cancel(loading_handle: int) -> int; +} +``` + +This keeps one deliberate naming asymmetry: + +- source owner: `LowAssets` +- runtime module/capability: `asset` + +That asymmetry looks acceptable because source declaration ownership is editorial, while `module/name/version` and capability strings are operational identities that must stay runtime-aligned. + +Tambem parece importante explicitar o limite desta agenda: + +- ela nao deve fechar a politica author-facing completa de asset references; +- ela deve produzir um alvo low-level suficiente para que a proxima decision possa dizer como `Assets.load(assets.foo.bar, slot)` baixa para a chamada host-backed final baseada em `asset_id`. + +## Resolution + +Accepted on 2026-03-27. + +Locked points: + +1. the low-level PBS owner is `LowAssets`; +2. the low-level declaration shape is `declare host LowAssets { ... }`; +3. the runtime binding stays on `module = "asset"` with capability `asset` in the singular; +4. v1 keeps raw `int` for `asset_id`, `slot`, `handle`, and status values; +5. any future symbolic author-facing asset surface must lower into this runtime-aligned low-level contract. diff --git a/discussion/workflow/decisions/DEC-0004-pbs-low-level-asset-manager-surface.md b/discussion/workflow/decisions/DEC-0004-pbs-low-level-asset-manager-surface.md new file mode 100644 index 00000000..42692884 --- /dev/null +++ b/discussion/workflow/decisions/DEC-0004-pbs-low-level-asset-manager-surface.md @@ -0,0 +1,176 @@ +--- +id: DEC-0004 +ticket: pbs-low-level-asset-manager-surface +title: PBS Low-Level Asset Manager Surface for Runtime AssetManager +status: accepted +created: 2026-03-27 +accepted: 2026-03-27 +agenda: AGD-0008 +plans: [PLN-0004] +tags: [compiler, pbs, runtime, asset-manager, host-abi, stdlib, asset] +--- + +## Decision + +PBS SHALL expose the runtime asset low-level surface through a reserved host-backed owner named `LowAssets`. + +The canonical v1 PBS declaration owner SHALL be: + +```pbs +declare host LowAssets { ... } +``` + +This owner name is editorial PBS surface only. +It MUST NOT change the canonical runtime identities published by `../runtime`. + +The low-level PBS asset surface SHALL bind to the current runtime syscall identities: + +- `("asset", "load", 1)` +- `("asset", "status", 1)` +- `("asset", "commit", 1)` +- `("asset", "cancel", 1)` + +The capability spelling for this surface SHALL be `asset`, in the singular: + +```pbs +[Capability(name = "asset")] +``` + +PBS SHALL NOT publish `assets` as the capability string for this low-level surface. + +For v1, the low-level PBS surface SHALL use raw `int` for: + +- `asset_id` +- `slot` +- `handle` +- operation and lifecycle status values + +The canonical illustrative shape is: + +```pbs +declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + + [Host(module = "asset", name = "commit", version = 1)] + [Capability(name = "asset")] + fn commit(loading_handle: int) -> int; + + [Host(module = "asset", name = "cancel", version = 1)] + [Capability(name = "asset")] + fn cancel(loading_handle: int) -> int; +} +``` + +## Rationale + +This decision locks the low-level PBS surface to the runtime contract that actually exists in the workspace on 2026-03-27. + +The runtime side has already converged on: + +- `asset.load(asset_id, slot) -> (status, handle)` +- `asset.status(handle) -> status` +- `asset.commit(handle) -> status` +- `asset.cancel(handle) -> status` + +The earlier name-based low-level load path is no longer the active runtime contract. +PBS SHOULD therefore stop treating that retired shape as a first-class candidate. + +`LowAssets` was chosen instead of `LowAsset` or `LowAssetManager` because: + +- it reads naturally beside higher-level surfaces such as a future `Assets` facade; +- it matches the user-facing plurality of the domain without changing runtime ABI identities; +- it keeps the source declaration owner concise. + +Raw `int` was chosen for v1 because: + +- the runtime ABI is slot-based and already integer-shaped; +- the immediate need is to publish a stable low-level bridge, not a richer type taxonomy; +- delaying the SDK interface until nominal wrappers are designed would block spec and implementation progress without changing executable semantics. + +Capability spelling remains singular `asset` because capability names are operational registry identities, not editorial surface names. +They MUST stay aligned with the runtime capability map. + +## Technical Specification + +### 1. Ownership And Binding + +- PBS low-level asset access MUST be declared through `declare host LowAssets`. +- Each member MUST carry a canonical `[Host(module = "asset", name = "...", version = 1)]` attribute. +- Each member MUST carry `[Capability(name = "asset")]`. +- PBS compilers and loaders MUST treat `module`, `name`, `version`, argument slots, return slots, and capability spelling as runtime-facing operational identity. + +### 2. ABI Shape + +The required v1 members are: + +1. `load(asset_id: int, slot: int) -> (status: int, loading_handle: int)` +2. `status(loading_handle: int) -> int` +3. `commit(loading_handle: int) -> int` +4. `cancel(loading_handle: int) -> int` + +Normative ABI correspondence: + +- `load` MUST lower to runtime syscall identity `("asset", "load", 1)` with `arg_slots = 2` and `ret_slots = 2`. +- `status` MUST lower to `("asset", "status", 1)` with `arg_slots = 1` and `ret_slots = 1`. +- `commit` MUST lower to `("asset", "commit", 1)` with `arg_slots = 1` and `ret_slots = 1`. +- `cancel` MUST lower to `("asset", "cancel", 1)` with `arg_slots = 1` and `ret_slots = 1`. + +### 3. Value Semantics + +- `asset_id` SHALL represent the runtime asset identity carried by `asset_table`. +- `slot` SHALL represent the target runtime slot index. +- `handle` SHALL represent the runtime load-operation handle returned by `load` when status is `OK`. +- PBS source signatures SHOULD use a non-keyword parameter label such as `loading_handle` for this value. +- status values SHALL remain raw integer domain values in the low-level surface for v1. + +PBS low-level callers MUST NOT pass: + +- `asset_name` +- `bank_type` +- any alternate symbolic asset reference directly to `LowAssets.load` + +The runtime resolves bank kind from `asset_table` using `asset_id`. +PBS low-level surface MUST preserve that rule. + +### 4. Relationship To Higher-Level Surfaces + +- This decision defines only the low-level host-backed asset surface. +- It does NOT define the final author-facing asset API. +- Any future symbolic PBS asset surface, including `Addressable` or `assets.foo.bar` style references, MUST lower to `asset_id` before the final `LowAssets.load(...)` host call. +- A future `declare service Assets` MAY wrap `LowAssets`, but such facade MUST NOT alter the canonical host identities defined here. + +### 5. Scope Boundary + +This decision covers: + +- the low-level owner name; +- capability spelling; +- canonical host bindings; +- raw value shape for v1. + +This decision does NOT yet cover: + +- nominal wrapper types for `AssetId`, `Handle`, or `SlotIndex`; +- enum surfaces for asset status values; +- `bank.info` / `bank.slot_info` integration; +- the author-facing symbolic assets surface from `AGD-0006`. + +## Constraints + +- PBS MUST stay aligned with the runtime capability registry and syscall registry as implemented in `../runtime`. +- PBS MUST NOT publish a plural capability string `assets` for this surface. +- PBS MUST NOT reintroduce a name-based low-level load contract into the canonical v1 PBS host surface. +- Any future editorial refinement to stronger low-level types MUST preserve the same runtime syscall identities unless a new decision explicitly revises this one. +- Plans and implementation derived from this decision MUST propagate updates to PBS specs and stdlib resources before introducing user-facing wrappers. + +## Revision Log + +- 2026-03-27: Initial accepted decision from AGD-0008. +- 2026-03-27: Linked execution plan PLN-0004. +- 2026-03-27: Refined illustrative PBS signatures to use named tuple returns and non-keyword handle labels without changing the locked runtime contract. diff --git a/discussion/workflow/plans/PLN-0004-pbs-low-level-asset-manager-surface.md b/discussion/workflow/plans/PLN-0004-pbs-low-level-asset-manager-surface.md new file mode 100644 index 00000000..696845b2 --- /dev/null +++ b/discussion/workflow/plans/PLN-0004-pbs-low-level-asset-manager-surface.md @@ -0,0 +1,194 @@ +--- +id: PLN-0004 +ticket: pbs-low-level-asset-manager-surface +title: Implement DEC-0004 PBS Low-Level Asset Manager Surface +status: done +created: 2026-03-27 +completed: 2026-03-27 +tags: [compiler, pbs, runtime, asset-manager, host-abi, stdlib, asset] +--- + +## Objective + +Propagate `DEC-0004` into PBS normative docs, stdlib interface resources, and compiler-facing tests so the workspace exposes a canonical low-level `LowAssets` host surface aligned with the current runtime asset ABI. + +## Background + +`DEC-0004` locked the v1 low-level PBS asset surface to: + +- `declare host LowAssets` +- runtime module `asset` +- capability `asset` +- raw `int` parameters and returns for `asset_id`, `slot`, `handle`, and status values +- runtime syscall identities: + - `asset.load(asset_id, slot) -> (status, handle)` + - `asset.status(handle) -> status` + - `asset.commit(handle) -> status` + - `asset.cancel(handle) -> status` + +The repository already ships comparable SDK interface modules for: + +- `@sdk:log` +- `@sdk:gfx` +- `@sdk:input` + +The repository does not yet ship `@sdk:asset`, and current PBS specs do not yet normatively pin `LowAssets` as the canonical low-level asset host owner. + +## Scope + +### Included +- Update PBS specs to include the low-level `LowAssets` asset host surface and its runtime alignment. +- Add a new stdlib interface module for `@sdk:asset`. +- Add compiler and stdlib-loading tests that pin the new interface module, reserved metadata, and import resolution behavior. +- Verify that the declared host ABI matches the runtime contract from `../runtime`. + +### Excluded +- Any author-facing `Assets` facade or `Addressable` lowering design from `DSC-0006`. +- Nominal wrapper types such as `AssetId`, `Handle`, or `SlotIndex`. +- Enumized status surfaces for asset operations. +- `bank.info` and `bank.slot_info` surfaces. +- Runtime changes in `../runtime`. + +## Execution Steps + +### Step 1 - Propagate The Decision Into PBS Specs + +**What:** +Update the normative PBS documentation so the low-level asset host surface is described explicitly and consistently with `DEC-0004`. + +**How:** +Add or revise sections that state: + +- the canonical owner name is `LowAssets`; +- the host bindings use `module = "asset"` and `Capability(name = "asset")`; +- the required members are `load`, `status`, `commit`, and `cancel`; +- `load` uses `asset_id` and `slot`, not `asset_name` or `bank_type`; +- the v1 surface uses raw `int` values; +- any future symbolic surface lowers into this low-level contract. + +Prioritize the spec surfaces that already define host ABI, stdlib packaging, and runtime capability mapping. + +**File(s):** +- `docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md` +- `docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md` +- `docs/specs/compiler-languages/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md` +- optional cross-reference if needed: `docs/specs/compiler/18. Standard Library Surface Specification.md` + +### Step 2 - Add The `@sdk:asset` Interface Module + +**What:** +Create the stdlib interface resources for the new low-level asset surface. + +**How:** +Add a new directory under the existing stdlib SDK layout mirroring the established `log` and `gfx` pattern: + +- `main.pbs` declares `LowAssets`; +- `mod.barrel` exports the low-level host owner; +- all members use canonical reserved metadata; +- signatures match the runtime ABI exactly: + - `fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int);` + - `fn status(loading_handle: int) -> int;` + - `fn commit(loading_handle: int) -> int;` + - `fn cancel(loading_handle: int) -> int;` + +Do not introduce a `declare service Assets` wrapper in this plan. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/mod.barrel` + +### Step 3 - Pin Interface Semantics And Metadata Extraction + +**What:** +Extend frontend tests so the reserved metadata and interface-module semantics recognize the new asset surface. + +**How:** +Add or update tests to cover: + +- parsing and semantic acceptance of `LowAssets` in `SourceKind.SDK_INTERFACE`; +- extraction of `[Host(...)]` and `[Capability(...)]` metadata for the new members; +- ABI slot counts for `load/status/commit/cancel`; +- rejection of incorrect legacy asset ABI shapes if they are declared as canonical. + +Reuse the same testing style already used for `LowLog` and `LowGfx`. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java` + +### Step 4 - Pin Stdlib Loading And Import Resolution + +**What:** +Verify that the compiler stdlib environment exposes `@sdk:asset` and that programs can import the surface through the normal interface-module loader path. + +**How:** +Add or update tests that: + +- resolve `@sdk:asset` through the stdlib loader; +- import `LowAssets` from `@sdk:asset`; +- ensure the imported surface compiles under the same reserved-host rules used by existing SDK modules; +- confirm capability and host identities survive the loading pipeline. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUStdlibCompileTest.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java` + +### Step 5 - Cross-Check Runtime ABI Alignment + +**What:** +Validate that the PBS declarations and tests remain aligned with the runtime ABI actually published by `../runtime`. + +**How:** +Before closing implementation, verify against the runtime sources currently present in the sibling workspace: + +- `module = "asset"` +- `Capability(name = "asset")` +- `load` uses `arg_slots = 2`, `ret_slots = 2` +- `status`, `commit`, and `cancel` use `arg_slots = 1`, `ret_slots = 1` + +If the runtime changes again before implementation lands, update the PBS work in the same change or stop and reopen the decision if the semantic contract changed. + +**File(s):** +- `../runtime/crates/console/prometeu-hal/src/syscalls/domains/asset.rs` +- `../runtime/docs/specs/runtime/15-asset-management.md` +- `../runtime/docs/specs/runtime/16-host-abi-and-syscalls.md` + +## Test Requirements + +### Unit Tests +- Interface-module semantics tests for `LowAssets` reserved metadata and signature acceptance. +- Frontend compiler tests for metadata extraction and ABI slot shape. +- Stdlib loader tests for `@sdk:asset` module discovery and barrel export resolution. + +### Integration Tests +- Frontend phase tests that compile a small source importing `LowAssets` from `@sdk:asset`. +- Conformance-style tests that assert the canonical host identities and capability spelling survive compile-time validation. + +### Manual Verification +- Inspect generated stdlib resource files for exact `Host` and `Capability` spellings. +- Re-run the targeted PBS frontend test suite covering stdlib loading, interface semantics, and reserved host metadata. +- Re-check the sibling runtime contract immediately before merge if implementation does not happen in the same working session. + +## Acceptance Criteria + +- [ ] PBS specs state that the canonical low-level asset owner is `LowAssets`. +- [ ] PBS specs state that capability spelling is `asset`, not `assets`. +- [ ] PBS specs state that `asset.load` uses `asset_id` and `slot`, not `asset_name` or `bank_type`. +- [ ] The stdlib environment exposes `@sdk:asset` with `declare host LowAssets`. +- [ ] `LowAssets.load/status/commit/cancel` signatures match the runtime ABI from `../runtime`. +- [ ] Compiler tests pin the reserved metadata, signature shapes, and import path for the new surface. +- [ ] No new higher-level `Assets` facade is introduced as part of this plan. + +## Dependencies + +- `DEC-0004-pbs-low-level-asset-manager-surface` +- Current runtime ABI as published in `../runtime` +- Existing stdlib interface loader and SDK resource layout in `prometeu-compiler/frontends/prometeu-frontend-pbs` + +## Risks + +- The sibling runtime may continue evolving, causing PBS docs or tests to drift if implementation is delayed. +- Introducing `@sdk:asset` may reveal hidden assumptions in stdlib packaging or SDK import tests that currently only cover `log`, `gfx`, and `input`. +- Because v1 keeps raw `int`, the first implementation may encourage accidental misuse of `asset_id`, `slot`, and `handle` in user-facing code if a higher-level wrapper is added too quickly later. diff --git a/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md b/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md index a1e88813..ae4cbb19 100644 --- a/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md +++ b/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md @@ -93,6 +93,13 @@ Rules: Source-level owners such as `Gfx` are SDK-facing aliases. They are not loader-facing identities. +For the current low-level asset surface: + +- the canonical PBS owner name is `LowAssets`, +- the canonical runtime module remains `asset`, +- the canonical capability remains `asset`, +- the canonical load identity is `("asset", "load", 1)`. + ## 6. Relationship to `declare host` `declare host` is a reserved SDK/toolchain surface used to expose host-backed APIs to source programs. @@ -129,6 +136,25 @@ but the PBX-facing declaration is canonical, for example: ("gfx", "draw_pixel", 1) ``` +The same rule applies to the low-level asset surface. +For example, the PBS-facing declaration: + +```pbs +declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); +} +``` + +maps to the canonical runtime identity: + +```text +("asset", "load", 1) +``` + +The loader MUST NOT derive runtime identity from the source owner spelling `LowAssets`. + ## 7. PBX Host Binding Section ### 7.1 Temporary section contract @@ -243,6 +269,13 @@ Rules: - successful resolution yields numeric syscall identifiers and associated metadata, - the resolved metadata is authoritative for capability gating and later runtime verification. +For the current asset runtime surface, the canonical registry entries include: + +- `("asset", "load", 1)` with `arg_slots = 2` and `ret_slots = 2`, +- `("asset", "status", 1)` with `arg_slots = 1` and `ret_slots = 1`, +- `("asset", "commit", 1)` with `arg_slots = 1` and `ret_slots = 1`, +- `("asset", "cancel", 1)` with `arg_slots = 1` and `ret_slots = 1`. + ## 10. Pre-Load Callsite Form The compiler emits host-backed call sites in pre-load form as: diff --git a/docs/specs/compiler-languages/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md b/docs/specs/compiler-languages/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md index 7c7e8ce5..b09ec14b 100644 --- a/docs/specs/compiler-languages/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md +++ b/docs/specs/compiler-languages/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md @@ -106,6 +106,9 @@ The current nominal capability set is: These names are the normative manifest-facing representation. +For the low-level PBS asset host surface, the capability spelling MUST remain `asset` in the singular. +PBS SDK owner names such as `LowAssets` do not change the manifest/runtime capability spelling. + ## 6. Relationship to Runtime `CapFlags` The runtime may continue to represent capabilities internally as a bitflag set. @@ -128,6 +131,8 @@ Canonical mapping: - `asset` -> `ASSET` - `bank` -> `BANK` +For asset host bindings, PBS declarations MUST use `[Capability(name = "asset")]` so that host admission, PBX host binding metadata, and cartridge capability checks all converge on the same runtime flag. + This means the recommended runtime design is: 1. deserialize `capabilities` into a nominal enum or equivalent validated representation, diff --git a/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md b/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md index 7cc51243..94370840 100644 --- a/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md +++ b/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md @@ -59,6 +59,9 @@ prometeu-compiler/frontends/prometeu-frontend-pbs/ gfx/ main.pbs mod.barrel + asset/ + main.pbs + mod.barrel audio/ main.pbs mod.barrel @@ -82,6 +85,7 @@ The compiler must treat the physical layout as a logical stdlib environment. Required mapping: - `stdlib//sdk/gfx` -> `@sdk:gfx` +- `stdlib//sdk/asset` -> `@sdk:asset` - `stdlib//sdk/audio` -> `@sdk:audio` - `stdlib//core/math` -> `@core:math` - `stdlib//core/math/vector` -> `@core:math/vector` @@ -204,6 +208,16 @@ declare host Gfx { } ``` +or: + +```pbs +declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); +} +``` + Rules: - the parser reads the attribute as part of the interface module source, @@ -211,6 +225,14 @@ Rules: - the compiler stores the extracted metadata in its interface graph, - the raw attribute surface is not treated as a runtime object, - later lowering stages may consume the extracted metadata to produce PBX host-binding declarations. +- stdlib line `1` MUST expose the low-level asset interface module at `@sdk:asset`, +- that module MUST declare `LowAssets`, +- that module MUST use runtime module `asset` and capability `asset`, +- that module MUST use the runtime-aligned signatures: + - `load(asset_id: int, slot: int) -> (status: int, loading_handle: int)` + - `status(loading_handle: int) -> int` + - `commit(loading_handle: int) -> int` + - `cancel(loading_handle: int) -> int` ## 10. Resources as an Implementation Strategy diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs new file mode 100644 index 00000000..862cc0a4 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs @@ -0,0 +1,17 @@ +declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + + [Host(module = "asset", name = "commit", version = 1)] + [Capability(name = "asset")] + fn commit(loading_handle: int) -> int; + + [Host(module = "asset", name = "cancel", version = 1)] + [Capability(name = "asset")] + fn cancel(loading_handle: int) -> int; +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/mod.barrel b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/mod.barrel new file mode 100644 index 00000000..ded86e1d --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/mod.barrel @@ -0,0 +1 @@ +pub host LowAssets; diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java index fcc0f99a..868a2a75 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java @@ -420,6 +420,44 @@ class PbsFrontendCompilerTest { assertEquals(1, fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().fields().size()); } + @Test + void shouldExtractLowAssetsReservedMetadataForInterfaceModuleSurface() { + final var source = """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + + [Host(module = "asset", name = "commit", version = 1)] + [Capability(name = "asset")] + fn commit(loading_handle: int) -> int; + + [Host(module = "asset", name = "cancel", version = 1)] + [Capability(name = "asset")] + fn cancel(loading_handle: int) -> int; + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var compiler = new PbsFrontendCompiler(); + final var fileBackend = compiler.compileFile(new FileId(11), source, diagnostics, SourceKind.SDK_INTERFACE); + + assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); + assertEquals(0, fileBackend.functions().size()); + assertEquals(4, fileBackend.reservedMetadata().hostMethodBindings().size()); + assertEquals(1, fileBackend.reservedMetadata().requiredCapabilities().size()); + assertEquals("asset", fileBackend.reservedMetadata().requiredCapabilities().getFirst()); + assertTrue(fileBackend.reservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowAssets") + && h.abiModule().equals("asset") + && h.abiMethod().equals("load") + && h.abiVersion() == 1)); + } + @Test void shouldRejectHostBindingWithoutCapabilityAtHostAdmission() { final var source = """ diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java index bc6deca3..30076ef4 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java @@ -87,10 +87,11 @@ class PbsGateUSdkInterfaceConformanceTest { import { Color } from @core:color; import { Gfx } from @sdk:gfx; import { Input, InputPad, InputButton } from @sdk:input; + import { LowAssets } from @sdk:asset; import { Log } from @sdk:log; declare contract Renderer { - fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, log: Log) -> void; + fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: LowAssets, log: Log) -> void; } """, "pub contract Renderer;", @@ -122,6 +123,11 @@ class PbsGateUSdkInterfaceConformanceTest { && h.abiModule().equals("log") && h.abiMethod().equals("write_tag") && h.abiVersion() == 1)); + assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowAssets") + && h.abiModule().equals("asset") + && h.abiMethod().equals("load") + && h.abiVersion() == 1)); final var negative = compileWorkspaceModule( tempDir.resolve("gate-u-reserved-import-negative"), @@ -320,6 +326,46 @@ class PbsGateUSdkInterfaceConformanceTest { DiagnosticPhase.HOST_ADMISSION); } + @Test + void gateU_shouldAcceptLowAssetsInterfaceSurfaceAndExposeCanonicalAssetBindings() { + final var source = """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + + [Host(module = "asset", name = "commit", version = 1)] + [Capability(name = "asset")] + fn commit(loading_handle: int) -> int; + + [Host(module = "asset", name = "cancel", version = 1)] + [Capability(name = "asset")] + fn cancel(loading_handle: int) -> int; + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var backend = new PbsFrontendCompiler().compileFile( + new FileId(43), + source, + diagnostics, + SourceKind.SDK_INTERFACE); + + assertFalse(diagnostics.hasErrors(), diagnostics.stream().map(Object::toString).toList().toString()); + assertEquals(4, backend.reservedMetadata().hostMethodBindings().size()); + assertEquals(1, backend.reservedMetadata().requiredCapabilities().size()); + assertEquals("asset", backend.reservedMetadata().requiredCapabilities().getFirst()); + assertTrue(backend.reservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowAssets") + && h.abiModule().equals("asset") + && h.abiMethod().equals("cancel") + && h.abiVersion() == 1)); + } + private WorkspaceCompileResult compileWorkspaceModule( final Path projectRoot, final String sourceContent, diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java index 45bedb48..f383f3d0 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java @@ -120,6 +120,26 @@ class PbsInterfaceModuleSemanticsTest { d.getCode().equals(PbsSemanticsErrors.E_SEM_INTERFACE_NON_DECLARATIVE_DECLARATION.name()))); } + @Test + void shouldAcceptLowAssetsHostSurfaceInInterfaceModule() { + final var source = """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + + [Host(module = "asset", name = "status", version = 1)] + [Capability(name = "asset")] + fn status(loading_handle: int) -> int; + } + """; + final var diagnostics = DiagnosticSink.empty(); + + new PbsFrontendCompiler().compileFile(new FileId(4), source, diagnostics, SourceKind.SDK_INTERFACE); + + assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); + } + @Test void shouldRejectBuiltinFieldWithNonAdmissibleLayoutType() { final var source = """ diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java index 5a4a5b82..d35c53ab 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java @@ -48,4 +48,39 @@ class InterfaceModuleLoaderTest { "pub host Gfx;", fileTable.get(module.barrelFiles().getFirst().fileId()).readUtf8().orElseThrow()); } + + @Test + void shouldLoadAssetStdlibModuleWithPublicHostBarrel() { + final var projectTable = new ProjectTable(); + final var projectId = projectTable.register(ProjectDescriptor.builder() + .name("app") + .version("1.0.0") + .rootPath(Path.of("/tmp/app")) + .sourceRoots(ReadOnlyList.wrap(java.util.List.of(Path.of("/tmp/app/src")))) + .build()); + final var fileTable = new FileTable(1); + final var diagnostics = DiagnosticSink.empty(); + final var moduleSource = new StdlibModuleSource( + "sdk", + ReadOnlyList.wrap(java.util.List.of("asset")), + ReadOnlyList.wrap(java.util.List.of(new StdlibModuleSource.SourceFile( + "main.pbs", + """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + } + """))), + "pub host LowAssets;"); + + final var module = new InterfaceModuleLoader().load(moduleSource, projectId, fileTable, diagnostics); + + assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); + assertEquals(1, module.sourceFiles().size()); + assertEquals(1, module.barrelFiles().size()); + assertEquals( + "pub host LowAssets;", + fileTable.get(module.barrelFiles().getFirst().fileId()).readUtf8().orElseThrow()); + } } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java index 6cf98622..2f508fcc 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java @@ -783,6 +783,61 @@ class PBSFrontendPhaseServiceTest { .anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("clear"))); } + @Test + void shouldResolveSdkAssetImportFromBootstrapStdlib() throws IOException { + final var projectRoot = tempDir.resolve("project-bootstrap-sdk-asset"); + final var sourceRoot = projectRoot.resolve("src"); + final var modulePath = sourceRoot.resolve("app"); + Files.createDirectories(modulePath); + + final var sourceFile = modulePath.resolve("source.pbs"); + final var modBarrel = modulePath.resolve("mod.barrel"); + Files.writeString(sourceFile, """ + import { LowAssets } from @sdk:asset; + declare contract Loader { + fn load(assets: LowAssets) -> void; + } + """); + Files.writeString(modBarrel, "pub contract Loader;"); + + final var projectTable = new ProjectTable(); + final var fileTable = new FileTable(1); + final var projectId = projectTable.register(ProjectDescriptor.builder() + .rootPath(projectRoot) + .name("app") + .version("1.0.0") + .sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot))) + .build()); + + registerFile(projectId, projectRoot, sourceFile, fileTable); + registerFile(projectId, projectRoot, modBarrel, fileTable); + + final var ctx = new FrontendPhaseContext( + projectTable, + fileTable, + new BuildStack(ReadOnlyList.wrap(List.of(projectId))), + 1); + final var diagnostics = DiagnosticSink.empty(); + + final var irBackend = new PBSFrontendPhaseService().compile( + ctx, + diagnostics, + LogAggregator.empty(), + BuildingIssueSink.empty()); + + assertTrue(diagnostics.stream().noneMatch(d -> + d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_MODULE_NOT_FOUND.name()))); + assertTrue(diagnostics.stream().noneMatch(d -> + d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_UNRESOLVED.name()))); + assertTrue(diagnostics.stream().noneMatch(d -> + d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name()))); + assertEquals(0, irBackend.getFunctions().size()); + assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowAssets") + && h.sourceMethodName().equals("load") + && h.abiModule().equals("asset"))); + } + @Test void shouldLowerSdkGfxServiceFacadeCallsWithoutHostCallableAmbiguity() throws IOException { final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-facade-call");