---
id: LSN-0024
ticket: pbs-game-facing-asset-refs-and-call-result-discard
title: Addressable Surface, Host Metadata, and Ignored-Value Discipline
created: 2026-03-30
tags: [compiler, pbs, asset-surface, lowering, diagnostics, runtime, host-metadata, ergonomics]
---

## Context

PBS needed to close two related gaps at the boundary between ergonomic game code and executable lowering.
Asset references needed an author-facing symbolic surface that did not fall back to unstable `asset_name` lookup or couple the frontend directly to packer internals.
At the same time, expression statements that produce values needed an explicit discard rule so backend stack discipline would not leak into source authoring.

The resulting implementation spans specs, frontend API models, backend lowering metadata, stdlib host surfaces, and compiler diagnostics.

## Key Decisions

### Keep Asset Authoring Symbolic But Backend-Owned

**What:**
PBS now treats `assets...` as a hierarchical compile-time namespace whose terminal leaves are `Addressable` values supplied through a backend-owned `FESurfaceContext`.
The v1 surface carries `List<Addressable(address, asset_id)>`.

**Why:**
This preserves natural source authoring while keeping the backend as the owner of operational asset identity and final `address -> asset_id` lowering.
The frontend gets enough structure for semantics, typing, and tooling without querying packer services directly.

**Trade-offs:**
Rename or move operations now change the canonical symbolic address and can break compile-time references.
That is stricter than a loose name-based model, but the breakage is explicit and deterministic instead of becoming late runtime ambiguity.

### Put Asset-Aware Lowering On Host Metadata, Not Service Bodies

**What:**
Asset-aware host calls now declare `[AssetLowering(param = N)]`, and backend lowering uses that metadata to rewrite the selected `Addressable` argument into runtime-facing `asset_id`.

**Why:**
This keeps lowering ownership on the host-backed contract surface instead of burying special behavior in wrapper services or generic expression shortcuts.
The lowering path stays aligned with `LowAssets`, and the compiler knows exactly which parameter is asset-facing.

**Trade-offs:**
Host-backed signatures must be validated more strictly, and metadata propagation has to remain consistent across extraction, indexing, and lowering.
That adds infrastructure, but it prevents the normative path from becoming implicit or drifting between layers.

### Treat Ignored Materialized Values As A General Language Rule

**What:**
Any PBS expression statement that produces a materialized value now triggers two things: a warning diagnostic and explicit discard lowering.

**Why:**
This keeps authoring simple without silently relying on backend stack behavior.
The language remains honest about ignored results while still allowing expression statements to execute.

**Trade-offs:**
The compiler now distinguishes unit-like results from materialized values in both semantics and lowering.
That is more work than silently discarding everything, but it yields a stable, testable language rule instead of API-specific exceptions.

## Patterns and Algorithms

- Use a backend-to-frontend surface contract when the frontend needs semantic visibility but must not own operational truth. `FESurfaceContext` is intentionally small and deduplicated by canonical address.
- Model symbolic assets as a namespace tree with terminal leaves only. Intermediate namespaces are not values, and terminal paths cannot also behave as namespace prefixes.
- Carry executable special cases as reserved host metadata. `[AssetLowering(param = N)]` is the authoritative marker for backend callsite rewriting.
- Make lowering side effects explicit when a source form hides operational cost. Ignored expression results are not silently tolerated; they lower to discard behavior and emit a warning.
- Pin cross-layer contracts in specs, stdlib surfaces, and compiler tests together. That prevents the PBS surface, host metadata, and runtime lowering path from drifting apart.

## Pitfalls

- Do not reintroduce `asset_name` as the operational compile-time identity. The symbolic surface is the canonical address derived from the asset root.
- Do not let the frontend query packer services directly for visible assets. It must consume the backend-owned surface contract.
- Do not bypass host metadata with generic expression rewriting for asset-aware calls. The normative path is host-backed `AssetLowering`.
- Do not treat intermediate `assets...` namespaces as terminal values, and do not allow terminal-vs-namespace collisions in asset roots.
- Do not silently drop ignored materialized values. If the compiler materializes a value in statement position, the discard path and warning are both part of the language contract.

## Takeaways

- PBS asset authoring is symbolic and hierarchical, but operational asset identity remains backend-owned and runtime-aligned through `asset_id`.
- Asset-aware lowering belongs to the host contract through reserved metadata, not to ad hoc wrapper logic.
- Ignored values are now a general PBS language rule: warn in semantics, discard explicitly in lowering.
- The durable pattern is consistent boundary ownership: frontend surfaces stay ergonomic, backend lowering stays authoritative, and the runtime contract remains explicit.
