updated packer agendas
This commit is contained in:
parent
4aef6cdaeb
commit
900a7e17dd
@ -0,0 +1,115 @@
|
|||||||
|
# Asset References in Game Code - Names vs Compile-Time Lowering Agenda
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Open
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Define how game code should reference assets over time:
|
||||||
|
|
||||||
|
- by logical `asset_name`,
|
||||||
|
- by compile-time lowering to stable `asset_id`,
|
||||||
|
- or through a more structured resource model inspired by engines that expose named resources while compiling them to stable internal handles.
|
||||||
|
|
||||||
|
## Domain Owner
|
||||||
|
|
||||||
|
`docs/compiler/pbs`
|
||||||
|
|
||||||
|
This is primarily a language/frontend and game-facing API question.
|
||||||
|
It depends on packer and runtime contracts, but it should be owned here because it affects source authoring, compile-time semantics, and generated runtime calls.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The current runtime still exposes game-facing asset operations by name.
|
||||||
|
|
||||||
|
Examples in `../runtime` today:
|
||||||
|
|
||||||
|
- graphics/runtime calls receive `asset_name`;
|
||||||
|
- audio/runtime calls receive `asset_name`;
|
||||||
|
- the runtime internally maps `asset_name -> asset_id` for normal asset use;
|
||||||
|
- preload/bootstrap was recently tightened around stable `asset_id`.
|
||||||
|
|
||||||
|
At the same time, the packer model already distinguishes:
|
||||||
|
|
||||||
|
- `asset_id` as stable artifact identity;
|
||||||
|
- `asset_name` as logical code-facing reference label.
|
||||||
|
|
||||||
|
This leaves an open architectural question:
|
||||||
|
|
||||||
|
Should game code continue to use strings/names directly forever, or should the compiler eventually lower asset references to more stable runtime-facing identifiers?
|
||||||
|
|
||||||
|
## Upstream Inputs
|
||||||
|
|
||||||
|
From packer decisions:
|
||||||
|
|
||||||
|
- [`001-asset-workspace-registry-and-stable-identity-decision.md`](../../../packer/decisions/001-asset-workspace-registry-and-stable-identity-decision.md)
|
||||||
|
- [`002-asset-specification-raw-assets-and-virtual-asset-contract-decision.md`](../../../packer/decisions/002-asset-specification-raw-assets-and-virtual-asset-contract-decision.md)
|
||||||
|
|
||||||
|
From runtime:
|
||||||
|
|
||||||
|
- `preload` now resolves by `asset_id`;
|
||||||
|
- asset APIs used by game/runtime code still resolve by `asset_name`;
|
||||||
|
- `asset_table` carries both `asset_id` and `asset_name`.
|
||||||
|
|
||||||
|
## Core Questions
|
||||||
|
|
||||||
|
1. Should source code asset references remain name-based in the long term?
|
||||||
|
2. Should the compiler resolve known asset references at compile time and lower them to `asset_id`?
|
||||||
|
3. If compile-time lowering exists, what happens for dynamic/late-bound asset names?
|
||||||
|
4. Should PBS expose a first-class resource handle abstraction instead of raw string references?
|
||||||
|
5. What compatibility and tooling story do we want for rename of `asset_name`?
|
||||||
|
6. How much runtime lookup by name is acceptable once asset identity is already stable in packer/runtime artifacts?
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Option A
|
||||||
|
|
||||||
|
Keep source and runtime calls name-based.
|
||||||
|
|
||||||
|
### Option B
|
||||||
|
|
||||||
|
Keep source authoring name-based, but lower resolvable references to `asset_id` at compile time.
|
||||||
|
|
||||||
|
### Option C
|
||||||
|
|
||||||
|
Introduce a richer resource abstraction in the language/toolchain, with compile-time or build-time resolution to stable handles under the hood.
|
||||||
|
|
||||||
|
## Tradeoffs
|
||||||
|
|
||||||
|
- Option A is simple and close to the current runtime, but keeps rename fragility and runtime lookup cost.
|
||||||
|
- Option B preserves authoring ergonomics while improving runtime determinism for static references.
|
||||||
|
- Option C could be the strongest long-term model, but it is also the most invasive and requires coordinated language, compiler, packer, and runtime work.
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Do not change the language surface blindly.
|
||||||
|
|
||||||
|
Recommended sequence:
|
||||||
|
|
||||||
|
1. preserve `asset_name` as the current source-level reference model;
|
||||||
|
2. evaluate compile-time lowering for statically known references as a separate capability;
|
||||||
|
3. only introduce a richer resource abstraction if the language ergonomics and tooling story justify the additional complexity.
|
||||||
|
|
||||||
|
This keeps current code understandable while leaving room to remove fragile runtime string lookup from hot paths later.
|
||||||
|
|
||||||
|
## Expected Decisions to Produce
|
||||||
|
|
||||||
|
1. Source-level asset reference model for PBS.
|
||||||
|
2. Whether compile-time lowering to `asset_id` should exist.
|
||||||
|
3. Boundaries between static references and dynamic references.
|
||||||
|
4. Rename/refactor expectations in Studio and compiler diagnostics.
|
||||||
|
5. Relationship between source references, packer outputs, and runtime lookup.
|
||||||
|
|
||||||
|
## Expected Spec Material
|
||||||
|
|
||||||
|
- PBS asset reference model spec or section.
|
||||||
|
- Compiler lowering rules for asset references.
|
||||||
|
- Diagnostics/refactoring behavior for asset rename.
|
||||||
|
- Cross-reference to packer/runtime asset identity contracts.
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- Defining every asset-related syscall in detail.
|
||||||
|
- Redesigning the entire runtime asset manager.
|
||||||
|
- Solving preload layout or `assets.pa` packing rules here.
|
||||||
@ -10,6 +10,7 @@ Closed agendas are moved to `docs/pbs/agendas/archive`.
|
|||||||
2. `18.1. Backend Workshop 1 - IRBackend Input Contract.md`
|
2. `18.1. Backend Workshop 1 - IRBackend Input Contract.md`
|
||||||
3. `18.2. Backend Workshop 2 - LowerToIRVM and IRVM Contract.md`
|
3. `18.2. Backend Workshop 2 - LowerToIRVM and IRVM Contract.md`
|
||||||
4. `18.3. Backend Workshop 3 - Bytecode Marshaling and Runtime Conformance.md`
|
4. `18.3. Backend Workshop 3 - Bytecode Marshaling and Runtime Conformance.md`
|
||||||
|
5. `18.4. Asset References in Game Code - Names vs Compile-Time Lowering Agenda.md`
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Open
|
Closed
|
||||||
|
|
||||||
|
Resolved by [`002-asset-specification-raw-assets-and-virtual-asset-contract-decision.md`](../decisions/002-asset-specification-raw-assets-and-virtual-asset-contract-decision.md).
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@ -28,6 +30,12 @@ Important example:
|
|||||||
- one `asset.json` describes the whole managed asset and its final packed output;
|
- one `asset.json` describes the whole managed asset and its final packed output;
|
||||||
- the internal source files are inputs of that asset, not separate assets unless explicitly modeled that way.
|
- the internal source files are inputs of that asset, not separate assets unless explicitly modeled that way.
|
||||||
|
|
||||||
|
The runtime-facing extraction path also depends on codec information.
|
||||||
|
Because codec affects how runtime opens, decodes, or materializes payload bytes, codec must be explicit in the asset contract rather than hidden inside format naming.
|
||||||
|
|
||||||
|
Preload intent is also asset-level information.
|
||||||
|
Each managed asset should carry an explicit entry indicating whether it participates in preload, so preload inclusion is declared intentionally rather than inferred indirectly later in the pipeline.
|
||||||
|
|
||||||
## Source Sections
|
## Source Sections
|
||||||
|
|
||||||
- `5.3 Asset Types (Bank Targets)`
|
- `5.3 Asset Types (Bank Targets)`
|
||||||
@ -44,6 +52,8 @@ Important example:
|
|||||||
5. Is `type` the bank target, the user-facing kind, or both?
|
5. Is `type` the bank target, the user-facing kind, or both?
|
||||||
6. How should packer version output formats independently from registry and descriptor schemas?
|
6. How should packer version output formats independently from registry and descriptor schemas?
|
||||||
7. How should one `asset.json` declare many internal inputs that together form one virtual asset, such as an atlas plus palettes?
|
7. How should one `asset.json` declare many internal inputs that together form one virtual asset, such as an atlas plus palettes?
|
||||||
|
8. Should codec be part of `output.format`, or a separate explicit field in the output contract?
|
||||||
|
9. How should each asset declare preload intent so the packer can materialize runtime preload entries deterministically?
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
@ -75,6 +85,38 @@ Also adopt this modeling direction:
|
|||||||
- many internal source files may feed that single managed output;
|
- many internal source files may feed that single managed output;
|
||||||
- atlas/image-bank style assets should be modeled as one asset with many inputs rather than many implicit sibling assets.
|
- atlas/image-bank style assets should be modeled as one asset with many inputs rather than many implicit sibling assets.
|
||||||
|
|
||||||
|
Also adopt this output-contract direction:
|
||||||
|
|
||||||
|
- `output.format` identifies the semantic/runtime format contract;
|
||||||
|
- `output.codec` identifies how payload bytes are stored for extraction/materialization;
|
||||||
|
- codec must remain explicit because it affects runtime extraction behavior;
|
||||||
|
- codec should not be smuggled into format naming when it represents a distinct storage concern.
|
||||||
|
|
||||||
|
Also adopt this preload direction:
|
||||||
|
|
||||||
|
- each managed asset must expose an explicit preload-related declaration entry;
|
||||||
|
- preload participation must be declared, not inferred from ad hoc conventions;
|
||||||
|
- packer build must use that declaration to decide whether preload data is emitted into the runtime-facing artifact contract.
|
||||||
|
- `preload.enabled` should be the required runtime-relevant boolean;
|
||||||
|
- richer packer-side preload metadata may exist, but it must not change runtime behavior until separately specified.
|
||||||
|
|
||||||
|
Current recommended shape:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"preload": {
|
||||||
|
"enabled": true,
|
||||||
|
"policy": "boot_critical"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Direction:
|
||||||
|
|
||||||
|
- `enabled` is the normative yes/no input for runtime preload emission;
|
||||||
|
- optional fields such as `policy` are packer-side hints for Studio, diagnostics, planning, or future evolution;
|
||||||
|
- packer-side preload hints must not silently alter the current runtime-facing preload contract.
|
||||||
|
|
||||||
## Expected Decisions to Produce
|
## Expected Decisions to Produce
|
||||||
|
|
||||||
1. Common `asset.json` schema boundary.
|
1. Common `asset.json` schema boundary.
|
||||||
@ -82,6 +124,8 @@ Also adopt this modeling direction:
|
|||||||
3. Versioning strategy for asset format specs.
|
3. Versioning strategy for asset format specs.
|
||||||
4. Rules for defaults and explicit materialization.
|
4. Rules for defaults and explicit materialization.
|
||||||
5. Multi-input asset declaration model for atlas, bank, and similar grouped assets.
|
5. Multi-input asset declaration model for atlas, bank, and similar grouped assets.
|
||||||
|
6. Explicit separation between output format and codec in the asset contract.
|
||||||
|
7. Asset-level preload declaration model.
|
||||||
|
|
||||||
## Expected Spec Follow-up
|
## Expected Spec Follow-up
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Open
|
Closed
|
||||||
|
|
||||||
|
Resolved by [`003-build-artifacts-and-deterministic-packing-decision.md`](../decisions/003-build-artifacts-and-deterministic-packing-decision.md).
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@ -67,36 +69,87 @@ From `../runtime/docs/runtime/specs/15-asset-management.md`:
|
|||||||
|
|
||||||
These are already normative runtime-facing requirements and should be treated as baseline constraints for packer output design.
|
These are already normative runtime-facing requirements and should be treated as baseline constraints for packer output design.
|
||||||
|
|
||||||
|
For this agenda, the intended direction is:
|
||||||
|
|
||||||
|
- `assets.pa` is the authoritative runtime-facing artifact;
|
||||||
|
- `build/asset_table.json` is a companion tooling artifact derived from the same build result;
|
||||||
|
- runtime must not depend on `build/asset_table.json` as its primary source of truth.
|
||||||
|
|
||||||
|
Current runtime code now resolves preload by `asset_id`, but game-facing runtime APIs still refer to assets by `asset_name`.
|
||||||
|
Therefore this agenda distinguishes:
|
||||||
|
|
||||||
|
- `asset_id` as stable artifact identity for preload/bootstrap and asset-table integrity;
|
||||||
|
- `asset_name` as logical API-level identifier used by game/runtime-facing calls today.
|
||||||
|
|
||||||
|
This agenda does not introduce a second runtime-only asset identity.
|
||||||
|
The `asset_id` emitted by the packer is the same `asset_id` used in the runtime-facing artifact contract.
|
||||||
|
|
||||||
## Key Questions
|
## Key Questions
|
||||||
|
|
||||||
1. How should the packer spec mirror the already normative `assets.pa` v1 envelope from runtime specs 13 and 15?
|
1. How should the packer spec mirror the already normative `assets.pa` v1 envelope from runtime specs 13 and 15?
|
||||||
2. Which fields in `asset_table.json` are still tooling-only if the runtime-facing contract lives primarily inside `assets.pa`?
|
2. Which fields in `build/asset_table.json` are companion tooling data versus runtime-facing contract data?
|
||||||
3. Is asset order always `asset_id` order, or can format-specific constraints override it?
|
3. Should global asset packing order be deterministic by `asset_id`, or influenced by usage and format-specific heuristics?
|
||||||
4. How should alignment be expressed and validated?
|
4. How should alignment be expressed and validated?
|
||||||
5. What exactly counts as determinism for virtual asset pipelines?
|
5. What exactly counts as determinism for virtual asset pipelines?
|
||||||
6. Which inferred values must be written back versus only emitted in build outputs?
|
6. Which inferred values must be written back versus only emitted in build outputs?
|
||||||
7. Which packer-side metadata is intentionally excluded from the runtime-facing contract?
|
7. Which packer-side metadata is intentionally excluded from the runtime-facing contract?
|
||||||
8. How should packer document `preload` lifecycle and `offset` semantics so they match runtime reader behavior?
|
8. How should packer document `preload` lifecycle and `offset` semantics so they match runtime reader behavior?
|
||||||
|
9. How should per-asset preload declarations be transformed into the `preload` section of `assets.pa` deterministically?
|
||||||
|
10. How should the packer preserve both `asset_id` integrity and `asset_name` lookup semantics in runtime-facing artifacts?
|
||||||
|
11. Should `asset_table` be modeled as a deterministically ordered asset list that preserves packer `asset_id` values end-to-end?
|
||||||
|
12. Which envelope fields are runtime-validated versus emitted only for Studio/tooling use?
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
### Option A
|
### Option A
|
||||||
|
|
||||||
Keep `assets.pa` simple and push structure into `asset_table.json`.
|
Keep `assets.pa` as the authoritative autocontained runtime-facing artifact and emit `build/asset_table.json` as a derived companion artifact for Studio and tooling.
|
||||||
|
|
||||||
### Option B
|
### Option B
|
||||||
|
|
||||||
Embed more self-description into `assets.pa` itself.
|
Keep runtime-facing structure split across `assets.pa` and `build/asset_table.json`.
|
||||||
|
|
||||||
## Tradeoffs
|
## Tradeoffs
|
||||||
|
|
||||||
- Option A keeps runtime payload compact and keeps metadata easier to evolve.
|
- Option A matches the runtime specs already in force.
|
||||||
- Option A matches the current draft direction and reduces double-modeling.
|
- Option A keeps runtime authority in one artifact and prevents contract drift.
|
||||||
- Option B may help standalone introspection, but it risks splitting authority across two artifacts.
|
- Option A still allows rich tooling through a derived companion file.
|
||||||
|
- Option B risks splitting authority across artifacts and making runtime/tooling divergence easier.
|
||||||
|
|
||||||
## Recommendation
|
## Recommendation
|
||||||
|
|
||||||
Adopt Option A and make `asset_table.json` the authoritative descriptor for ROM slice structure.
|
Adopt Option A with these fixed directions:
|
||||||
|
|
||||||
|
- `assets.pa` is the authoritative runtime-facing artifact;
|
||||||
|
- the `assets.pa` header is the authoritative runtime-facing descriptor;
|
||||||
|
- `build/asset_table.json` is a derived companion artifact for Studio, debugging, inspection, and builder/tooling integration;
|
||||||
|
- global packing order is deterministic by increasing `asset_id`;
|
||||||
|
- preload emission is derived deterministically from per-asset `preload.enabled`;
|
||||||
|
- alignment exists only when explicitly required by spec;
|
||||||
|
- offsets are always relative to the payload region, never to the start of the full file;
|
||||||
|
- checksum is not part of the baseline `assets.pa` envelope contract.
|
||||||
|
|
||||||
|
Also adopt this performance direction:
|
||||||
|
|
||||||
|
- usage-based hot-first packing is not part of the baseline contract;
|
||||||
|
- deterministic stable ordering is preferred over heuristic physical reordering;
|
||||||
|
- future locality optimization may be introduced only through an explicit later decision and spec, not as an implementation shortcut now.
|
||||||
|
|
||||||
|
Also adopt this identity direction:
|
||||||
|
|
||||||
|
- runtime-facing preload entries should migrate to stable `asset_id` instead of mutable `asset_name`;
|
||||||
|
- runtime-facing preload/bootstrap integrity should treat `asset_id` as primary identity;
|
||||||
|
- `asset_name` remains present in `asset_table` as logical descriptive and API-facing metadata used by current runtime/game-facing calls;
|
||||||
|
- `asset_table` should be generated deterministically on every build from the current managed asset set;
|
||||||
|
- `asset_table` should be emitted as a deterministically ordered list of asset entries;
|
||||||
|
- the runtime-facing `asset_id` is exactly the stable `asset_id` allocated by the packer registry;
|
||||||
|
- stable cross-build identity comes from `asset_id`, not from table position;
|
||||||
|
- rename of `asset_name` is therefore an API-visible/content-visible change, but not an identity change.
|
||||||
|
|
||||||
|
Also adopt this companion-artifact direction:
|
||||||
|
|
||||||
|
- `build/asset_table.json` should mirror the runtime-facing header data 1:1 for debugging and inspection;
|
||||||
|
- if richer tooling artifacts are needed, they should be emitted as separate files instead of overloading `build/asset_table.json`.
|
||||||
|
|
||||||
## Expected Decisions to Produce
|
## Expected Decisions to Produce
|
||||||
|
|
||||||
@ -106,6 +159,11 @@ Adopt Option A and make `asset_table.json` the authoritative descriptor for ROM
|
|||||||
4. Materialization requirements for inferred values.
|
4. Materialization requirements for inferred values.
|
||||||
5. Runtime-facing artifact contract and reader expectations.
|
5. Runtime-facing artifact contract and reader expectations.
|
||||||
6. Packer-side restatement of the runtime-owned `assets.pa` envelope and slice semantics.
|
6. Packer-side restatement of the runtime-owned `assets.pa` envelope and slice semantics.
|
||||||
|
7. Deterministic mapping from asset declarations to runtime preload data.
|
||||||
|
8. Explicit exclusion of usage-based packing heuristics from the baseline contract.
|
||||||
|
9. Migration of preload/bootstrap integrity toward `asset_id`-based identity while preserving `asset_name` as current API-facing metadata.
|
||||||
|
10. Deterministically ordered `asset_table` preserving packer `asset_id` values end-to-end.
|
||||||
|
11. Separation between runtime-validated envelope fields and non-envelope tooling concerns.
|
||||||
|
|
||||||
## Expected Spec Follow-up
|
## Expected Spec Follow-up
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,14 @@ The following are not primary identity:
|
|||||||
|
|
||||||
Name and path are mutable presentation or location fields.
|
Name and path are mutable presentation or location fields.
|
||||||
|
|
||||||
|
However, `asset_name` may still participate in authoring and runtime-facing APIs as a logical reference label.
|
||||||
|
That does not make it the stable identity of the asset.
|
||||||
|
|
||||||
|
Practical consequence:
|
||||||
|
|
||||||
|
- changing `asset_name` does not create a new asset identity;
|
||||||
|
- but changing `asset_name` may still require source-level or tooling-level reference updates where game/runtime APIs refer to assets by name.
|
||||||
|
|
||||||
### 5. Relocation and rename
|
### 5. Relocation and rename
|
||||||
|
|
||||||
Moving or renaming an asset root does not create a new asset.
|
Moving or renaming an asset root does not create a new asset.
|
||||||
|
|||||||
@ -0,0 +1,282 @@
|
|||||||
|
# Asset Specification, Raw Assets, and Virtual Asset Contract Decision
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Date
|
||||||
|
|
||||||
|
2026-03-11
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This decision closes the architectural questions raised in [`01.2. Asset Specification, Raw Assets, and Virtual Asset Contract Agenda`](../agendas/01.2.%20Asset%20Specification,%20Raw%20Assets,%20and%20Virtual%20Asset%20Contract%20Agenda.md).
|
||||||
|
|
||||||
|
The packer needs a stable baseline contract for `asset.json` before detailed format-specific specs are written.
|
||||||
|
|
||||||
|
This contract must:
|
||||||
|
|
||||||
|
- support one managed asset root with many internal inputs;
|
||||||
|
- distinguish raw assets from virtual assets without making the common schema monolithic;
|
||||||
|
- expose runtime-relevant output information explicitly;
|
||||||
|
- declare preload intent deterministically;
|
||||||
|
- leave room for future format families without coupling all formats together.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
The packer adopts a compact common `asset.json` contract with explicit separation between:
|
||||||
|
|
||||||
|
- authoring asset family;
|
||||||
|
- grouped input declaration;
|
||||||
|
- runtime-facing output contract;
|
||||||
|
- preload declaration;
|
||||||
|
- optional build/process hints.
|
||||||
|
|
||||||
|
### 1. Common top-level shape
|
||||||
|
|
||||||
|
The common `asset.json` contract includes these required top-level fields:
|
||||||
|
|
||||||
|
- `schema_version`
|
||||||
|
- `name`
|
||||||
|
- `type`
|
||||||
|
- `inputs`
|
||||||
|
- `output`
|
||||||
|
- `preload`
|
||||||
|
|
||||||
|
The common contract may additionally include:
|
||||||
|
|
||||||
|
- `build`
|
||||||
|
|
||||||
|
`build` is optional in the shared baseline schema.
|
||||||
|
|
||||||
|
### 2. Meaning of `type`
|
||||||
|
|
||||||
|
`type` identifies the authoring-side asset family.
|
||||||
|
|
||||||
|
It is not the runtime bank target.
|
||||||
|
|
||||||
|
Examples of valid family-style values:
|
||||||
|
|
||||||
|
- `image_bank`
|
||||||
|
- `sound_bank`
|
||||||
|
|
||||||
|
The runtime-facing technical target belongs in `output.format`, not in `type`.
|
||||||
|
|
||||||
|
`name` remains the required logical asset reference label.
|
||||||
|
|
||||||
|
Meaning:
|
||||||
|
|
||||||
|
- `name` is not the stable artifact identity;
|
||||||
|
- `name` is the human-facing and code-facing reference used by asset-oriented APIs unless a future compile-time rewrite model replaces it;
|
||||||
|
- renaming `name` is an API-visible change even though it does not change `asset_id`.
|
||||||
|
|
||||||
|
### 3. Input declaration model
|
||||||
|
|
||||||
|
`inputs` is a structured object keyed by semantic role.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- each key identifies an input role such as `sprites`, `palettes`, or `sources`;
|
||||||
|
- each value is a list of paths;
|
||||||
|
- paths are relative to the asset root;
|
||||||
|
- even a single input is represented as a list, not as a scalar.
|
||||||
|
|
||||||
|
This avoids shape ambiguity and supports grouped virtual assets cleanly.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"inputs": {
|
||||||
|
"sprites": [
|
||||||
|
"sprites/confirm.png",
|
||||||
|
"sprites/cancel.png"
|
||||||
|
],
|
||||||
|
"palettes": [
|
||||||
|
"palettes/ui_main.pal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Output contract
|
||||||
|
|
||||||
|
`output` is the runtime-relevant output declaration.
|
||||||
|
|
||||||
|
The common baseline requires:
|
||||||
|
|
||||||
|
- `output.format`
|
||||||
|
- `output.codec`
|
||||||
|
|
||||||
|
`output.metadata` is optional in the common schema, but becomes required whenever the selected format spec requires additional normative parameters.
|
||||||
|
|
||||||
|
Meaning:
|
||||||
|
|
||||||
|
- `output.format` identifies the semantic/runtime format contract;
|
||||||
|
- `output.codec` identifies how payload bytes are stored for extraction and materialization;
|
||||||
|
- `output.metadata` carries format-specific runtime-relevant details.
|
||||||
|
|
||||||
|
Codec must remain explicit.
|
||||||
|
It must not be hidden inside format naming when it represents a distinct storage concern.
|
||||||
|
|
||||||
|
### 5. Raw and virtual asset contract
|
||||||
|
|
||||||
|
The common schema must support both raw and virtual assets.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- raw assets may declare a direct input-to-output path with minimal build metadata;
|
||||||
|
- virtual assets may declare multiple grouped inputs and optional build/process configuration;
|
||||||
|
- the common schema remains small, while detailed format behavior is defined in dedicated format specs.
|
||||||
|
|
||||||
|
### 6. Build/process hints
|
||||||
|
|
||||||
|
`build` is optional and carries process-oriented configuration.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `build` may describe how the packer should transform or organize authoring inputs;
|
||||||
|
- if a parameter affects the runtime-facing output contract, it belongs in `output.metadata`;
|
||||||
|
- `build` must not become a hidden substitute for runtime-relevant output definition.
|
||||||
|
|
||||||
|
Practical interpretation:
|
||||||
|
|
||||||
|
- `output.metadata` describes the contract of the produced payload;
|
||||||
|
- `build` describes how the packer gets there.
|
||||||
|
|
||||||
|
### 7. Preload declaration
|
||||||
|
|
||||||
|
Each managed asset must declare preload intent explicitly.
|
||||||
|
|
||||||
|
The baseline shape is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"preload": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `preload.enabled` is required and boolean;
|
||||||
|
- preload participation is declared, not inferred;
|
||||||
|
- packer build uses this field to determine whether preload data is emitted into the runtime-facing artifact contract;
|
||||||
|
- richer preload policy fields are deferred to future decisions and must not be implied silently now.
|
||||||
|
|
||||||
|
### 8. Defaults and materialization
|
||||||
|
|
||||||
|
Defaults may exist in specs, but defaults that affect reproducibility, compatibility, preload, or runtime-visible output must not remain invisible.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- runtime-relevant defaults should be materialized in `asset.json` whenever practical;
|
||||||
|
- if materialization occurs later in the pipeline, the resulting artifact must still expose the effective value explicitly;
|
||||||
|
- packer must not rely on hidden defaults to produce runtime-visible behavior.
|
||||||
|
|
||||||
|
### 9. Versioning boundary
|
||||||
|
|
||||||
|
Versioning is split by concern.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `asset.json` carries its own `schema_version`;
|
||||||
|
- registry schemas are versioned independently;
|
||||||
|
- runtime-facing artifact schemas are versioned independently;
|
||||||
|
- format contracts are versioned independently through `output.format` values such as `TILES/indexed_v1` or `SOUNDS/pcm16le_v1`.
|
||||||
|
|
||||||
|
This prevents unrelated schema evolution from being coupled together.
|
||||||
|
|
||||||
|
## Invariants and Constraints
|
||||||
|
|
||||||
|
The following invariants now apply:
|
||||||
|
|
||||||
|
1. The common `asset.json` contract remains compact.
|
||||||
|
2. `type` is authoring-side family identity, not runtime bank identity.
|
||||||
|
3. `inputs` is structured by semantic role.
|
||||||
|
4. Input path values are always lists.
|
||||||
|
5. `name` remains a logical asset reference label even though it is not the stable artifact identity.
|
||||||
|
6. `output.format` and `output.codec` are separate required concerns.
|
||||||
|
7. Runtime-relevant format details belong in `output.metadata`.
|
||||||
|
8. `build` is optional and must not hide runtime-facing output semantics.
|
||||||
|
9. `preload.enabled` is the only preload field closed in the current baseline.
|
||||||
|
10. Defaults affecting runtime-visible behavior must be materialized explicitly.
|
||||||
|
11. Format evolution is versioned independently from registry and artifact schema evolution.
|
||||||
|
|
||||||
|
## Explicit Non-Decisions
|
||||||
|
|
||||||
|
This decision does not yet define:
|
||||||
|
|
||||||
|
- the full field-level schema of each format family;
|
||||||
|
- richer preload policies beyond `preload.enabled`;
|
||||||
|
- globbing support, input discovery shortcuts, or non-path input locators;
|
||||||
|
- CLI or Studio editor UX for authoring `asset.json`;
|
||||||
|
- exact `assets.pa` payload layout details;
|
||||||
|
- exact runtime preload record encoding.
|
||||||
|
|
||||||
|
Those belong to later decisions and specs.
|
||||||
|
|
||||||
|
## Propagation Targets
|
||||||
|
|
||||||
|
This decision must propagate to:
|
||||||
|
|
||||||
|
- common asset declaration spec;
|
||||||
|
- virtual asset contract spec;
|
||||||
|
- format-specific specs such as `TILES/indexed_v1` and `SOUNDS/pcm16le_v1`;
|
||||||
|
- preload emission and artifact mapping specs;
|
||||||
|
- Studio service contracts for asset authoring and validation;
|
||||||
|
- future implementation of asset parsing, validation, and deterministic build planning.
|
||||||
|
|
||||||
|
## Validation Notes
|
||||||
|
|
||||||
|
Example: grouped image asset
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"name": "ui_atlas",
|
||||||
|
"type": "image_bank",
|
||||||
|
"inputs": {
|
||||||
|
"sprites": [
|
||||||
|
"sprites/confirm.png",
|
||||||
|
"sprites/cancel.png"
|
||||||
|
],
|
||||||
|
"palettes": [
|
||||||
|
"palettes/ui_main.pal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"format": "TILES/indexed_v1",
|
||||||
|
"codec": "RAW",
|
||||||
|
"metadata": {
|
||||||
|
"tile_size": [8, 8]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preload": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"layout": "atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: grouped sound asset
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"name": "ui_sounds",
|
||||||
|
"type": "sound_bank",
|
||||||
|
"inputs": {
|
||||||
|
"sources": [
|
||||||
|
"wav/click.wav",
|
||||||
|
"wav/confirm.wav"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"format": "SOUNDS/pcm16le_v1",
|
||||||
|
"codec": "RAW"
|
||||||
|
},
|
||||||
|
"preload": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -0,0 +1,250 @@
|
|||||||
|
# Build Artifacts and Deterministic Packing Decision
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Date
|
||||||
|
|
||||||
|
2026-03-11
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This decision closes the architectural questions raised in [`01.3. Build Artifacts and Deterministic Packing Agenda`](../agendas/01.3.%20Build%20Artifacts%20and%20Deterministic%20Packing%20Agenda.md).
|
||||||
|
|
||||||
|
The packer must produce build artifacts that are:
|
||||||
|
|
||||||
|
- deterministic;
|
||||||
|
- aligned with the runtime-facing `assets.pa` contract already established in `../runtime`;
|
||||||
|
- usable by Studio and tooling without creating a second source of truth;
|
||||||
|
- stable enough to support future specs and implementation.
|
||||||
|
|
||||||
|
The runtime-side contract already establishes that:
|
||||||
|
|
||||||
|
- `assets.pa` is the runtime-facing artifact;
|
||||||
|
- `asset_table` and `preload` live in the internal JSON header;
|
||||||
|
- offsets are relative to the payload region;
|
||||||
|
- preload is boot-time input only.
|
||||||
|
|
||||||
|
This decision closes how the packer should produce these artifacts.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
The packer adopts an `assets.pa`-authoritative, deterministically ordered, canonical-header model.
|
||||||
|
|
||||||
|
### 1. Artifact authority
|
||||||
|
|
||||||
|
`assets.pa` is the authoritative runtime-facing artifact.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- the `assets.pa` header is the runtime-facing source of truth for `asset_table` and `preload`;
|
||||||
|
- `build/asset_table.json` and other JSON outputs are companion artifacts only;
|
||||||
|
- runtime must not depend on companion JSON files as its primary contract.
|
||||||
|
|
||||||
|
### 2. Companion artifacts
|
||||||
|
|
||||||
|
The packer emits separate companion artifacts for tooling and debugging.
|
||||||
|
|
||||||
|
Baseline companion outputs:
|
||||||
|
|
||||||
|
- `build/asset_table.json`
|
||||||
|
- `build/preload.json`
|
||||||
|
- `build/asset_table_metadata.json`
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `build/asset_table.json` mirrors `header.asset_table` 1:1;
|
||||||
|
- `build/preload.json` mirrors `header.preload` 1:1;
|
||||||
|
- `build/asset_table_metadata.json` is tooling-only and may carry richer packer metadata;
|
||||||
|
- richer tooling data must not be smuggled into the runtime-facing mirror files.
|
||||||
|
|
||||||
|
### 3. `assets.pa` v1 prelude
|
||||||
|
|
||||||
|
The baseline packer-side prelude for `assets.pa` v1 includes:
|
||||||
|
|
||||||
|
- `magic`
|
||||||
|
- `schema_version`
|
||||||
|
- `header_len`
|
||||||
|
- `payload_offset`
|
||||||
|
- `flags`
|
||||||
|
- `reserved`
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `flags` exists from day 1 for forward compatibility;
|
||||||
|
- `reserved` exists from day 1 for forward compatibility;
|
||||||
|
- `flags = 0` in the current baseline unless a future spec says otherwise;
|
||||||
|
- `reserved = 0` in the current baseline unless a future spec says otherwise;
|
||||||
|
- `header_checksum` is not part of the baseline `assets.pa` envelope contract.
|
||||||
|
|
||||||
|
### 4. Checksum policy
|
||||||
|
|
||||||
|
Header checksum is not part of the baseline runtime-facing envelope.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- runtime validation does not depend on header checksum;
|
||||||
|
- cartridge-level integrity may be handled elsewhere;
|
||||||
|
- Studio/tooling may compute checks or hashes separately if needed;
|
||||||
|
- such checks must not redefine the runtime-facing `assets.pa` envelope.
|
||||||
|
|
||||||
|
### 5. Canonical JSON header
|
||||||
|
|
||||||
|
The JSON header of `assets.pa` must be serialized canonically.
|
||||||
|
|
||||||
|
Canonicalization rules:
|
||||||
|
|
||||||
|
- UTF-8 encoding;
|
||||||
|
- no extra whitespace;
|
||||||
|
- object keys sorted lexicographically;
|
||||||
|
- arrays preserve declared order;
|
||||||
|
- canonicalization applies recursively, including nested metadata objects;
|
||||||
|
- runtime-facing header values in v1 must avoid floating-point numbers.
|
||||||
|
|
||||||
|
This is required so equivalent inputs produce byte-identical headers and therefore byte-reproducible artifacts.
|
||||||
|
|
||||||
|
### 6. Ordering and determinism
|
||||||
|
|
||||||
|
The global `asset_table` order is deterministic by increasing `asset_id`.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- usage-based or hot-first packing is not part of the baseline contract;
|
||||||
|
- format-specific heuristics must not reorder the global asset list;
|
||||||
|
- deterministic ordering is preferred over physical-layout heuristics;
|
||||||
|
- future locality optimization requires a separate explicit decision and spec.
|
||||||
|
|
||||||
|
### 7. `asset_id` continuity
|
||||||
|
|
||||||
|
The `asset_id` used in runtime-facing artifacts is the same stable `asset_id` allocated by the packer registry.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- the packer does not generate a second runtime-only asset identity;
|
||||||
|
- `preload` refers to the same `asset_id` values that appear in `asset_table`;
|
||||||
|
- table position does not define identity;
|
||||||
|
- cross-build identity stability comes from `asset_id`, not from table index.
|
||||||
|
|
||||||
|
### 8. Asset table shape
|
||||||
|
|
||||||
|
`asset_table` is emitted as a deterministically ordered list of asset entries.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- one emitted entry per managed asset in the build set;
|
||||||
|
- no synthetic dense reindexing layer is introduced;
|
||||||
|
- omission of removed assets happens naturally because the emitted table reflects the current build set only;
|
||||||
|
- the runtime sees a normal contiguous list of entries, but the IDs inside it remain the stable packer IDs.
|
||||||
|
|
||||||
|
### 9. `asset_name` in runtime-facing artifacts
|
||||||
|
|
||||||
|
`asset_name` remains present in `asset_table`.
|
||||||
|
|
||||||
|
Meaning:
|
||||||
|
|
||||||
|
- `asset_name` is logical descriptive and API-facing metadata;
|
||||||
|
- current runtime/game-facing calls may still use it for normal asset lookup;
|
||||||
|
- preload/bootstrap integrity relies on `asset_id`, not `asset_name`;
|
||||||
|
- renaming `asset_name` is an API-visible/content-visible change, but not an asset identity change.
|
||||||
|
|
||||||
|
### 10. Preload mapping
|
||||||
|
|
||||||
|
Preload emission is derived deterministically from per-asset declaration.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- assets with `preload.enabled = false` do not appear in emitted preload data;
|
||||||
|
- assets with `preload.enabled = true` produce preload entries in the runtime-facing preload list;
|
||||||
|
- emitted preload ordering is deterministic by increasing `asset_id`;
|
||||||
|
- preload is boot-time input only and must be emitted in a form consistent with the runtime contract.
|
||||||
|
|
||||||
|
### 11. Alignment
|
||||||
|
|
||||||
|
Alignment exists only when explicitly required by spec.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- there is no implicit baseline alignment beyond what is required by the envelope and chosen formats;
|
||||||
|
- if a format requires alignment, the requirement must be normative and visible in the relevant spec;
|
||||||
|
- computed offsets must always be emitted explicitly in the runtime-facing descriptor.
|
||||||
|
|
||||||
|
### 12. Offsets
|
||||||
|
|
||||||
|
All emitted asset offsets are relative to the payload region.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- offsets are never relative to the start of the whole `assets.pa` file;
|
||||||
|
- this rule must be preserved consistently across packer outputs and specs;
|
||||||
|
- companion artifacts must mirror the same offset semantics.
|
||||||
|
|
||||||
|
## Invariants and Constraints
|
||||||
|
|
||||||
|
The following invariants now apply:
|
||||||
|
|
||||||
|
1. `assets.pa` is the authoritative runtime-facing artifact.
|
||||||
|
2. Companion JSON files do not replace the internal `assets.pa` header.
|
||||||
|
3. `build/asset_table.json` mirrors `header.asset_table` 1:1.
|
||||||
|
4. `build/preload.json` mirrors `header.preload` 1:1.
|
||||||
|
5. `build/asset_table_metadata.json` is tooling-only.
|
||||||
|
6. The prelude includes `magic`, `schema_version`, `header_len`, `payload_offset`, `flags`, and `reserved`.
|
||||||
|
7. `header_checksum` is not part of the baseline envelope contract.
|
||||||
|
8. Header JSON is canonicalized.
|
||||||
|
9. Global asset ordering is deterministic by increasing `asset_id`.
|
||||||
|
10. The runtime-facing `asset_id` is the same stable packer `asset_id`.
|
||||||
|
11. `asset_table` is a deterministically ordered list, not a second identity system.
|
||||||
|
12. `asset_name` remains present as logical/API-facing metadata.
|
||||||
|
13. Preload is emitted deterministically from `preload.enabled`.
|
||||||
|
14. Offsets are relative to the payload region only.
|
||||||
|
|
||||||
|
## Explicit Non-Decisions
|
||||||
|
|
||||||
|
This decision does not yet define:
|
||||||
|
|
||||||
|
- the exact field-level metadata contract of each output format;
|
||||||
|
- richer preload policies beyond the current baseline;
|
||||||
|
- future physical locality optimization;
|
||||||
|
- game-language lowering of asset references from names to IDs;
|
||||||
|
- cartridge-level integrity/signature strategy.
|
||||||
|
|
||||||
|
Those belong to later decisions and specs.
|
||||||
|
|
||||||
|
## Propagation Targets
|
||||||
|
|
||||||
|
This decision must propagate to:
|
||||||
|
|
||||||
|
- packer artifact and envelope specs;
|
||||||
|
- packer determinism and reproducibility specs;
|
||||||
|
- packer preload emission specs;
|
||||||
|
- Studio tooling expectations for companion artifacts;
|
||||||
|
- future implementation of canonical header generation and companion-file emission.
|
||||||
|
|
||||||
|
## Validation Notes
|
||||||
|
|
||||||
|
Example: artifact relationship
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets.pa
|
||||||
|
prelude
|
||||||
|
canonical header JSON
|
||||||
|
asset_table
|
||||||
|
preload
|
||||||
|
payload bytes
|
||||||
|
|
||||||
|
build/asset_table.json
|
||||||
|
mirrors header.asset_table
|
||||||
|
|
||||||
|
build/preload.json
|
||||||
|
mirrors header.preload
|
||||||
|
|
||||||
|
build/asset_table_metadata.json
|
||||||
|
tooling-only enrichment
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: stable identity
|
||||||
|
|
||||||
|
- packer registry assigns asset IDs `3`, `7`, and `11`
|
||||||
|
- emitted `asset_table` is ordered `[3, 7, 11]`
|
||||||
|
- runtime-facing preload entries reference `3`, `7`, or `11`
|
||||||
|
- no additional dense runtime ID layer is introduced
|
||||||
Loading…
x
Reference in New Issue
Block a user