prometeu-studio/docs/packer/decisions/003-build-artifacts-and-deterministic-packing-decision.md
2026-03-24 13:42:38 +00:00

251 lines
8.1 KiB
Markdown

# 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