Assets
Logical asset IDs, the asynchronous load queue, typed decoders, manifests and
hot reload. docs/asset-foundation.md is the contract.
Each entry links its tests and the projects that exercise it. Surface marks match the row in parity where one is named: โ works and is exercised ยท ๐ก a useful slice, with the gap named ยท โ absent ยท โ not applicable.
| Capability | Engine | Editor | Decay | Proof |
|---|---|---|---|---|
| Loading | ||||
| Asset IDs, manifests, async load | โ | โ | ๐ก | โ |
| Identical image decoding on every target | โ | โ | โ | โ |
| Project fonts | โ | โ | โ | โ |
| Reusable data profiles | โ | โ | โ | โ |
| Hot reload | โ | ๐ก | ๐ก | โ |
Loading
Asset IDs, manifests, async load
Validated logical IDs, a bounded generation-checked load queue, typed decoders and hash-checked manifests.
Engine โ ยท Editor โ ยท Decay ๐ก ยท Proof โ โ parity row Asset IDs, manifests, async load
Tested by: crates/sindri-core/src/asset/tests/id.rs, crates/sindri-core/src/asset/tests/store.rs, crates/sindri-assets/src/queue/tests.rs, crates/sindri-assets/src/loader/tests.rs, crates/sindri-assets/src/manifest/tests.rs, crates/sindri-gpu/tests/texture_registry.rs
Exercised in: games/orbital-baked
Evidence
AssetId is a validated relative logical ID, never a path or URL. Sources
resolve it against a root: the filesystem source canonicalises to catch symlink
escapes, and UrlRoot percent-encodes and normalises bases with rules
exercised on every target rather than only in a browser. The load queue is
bounded, rejects duplicates, and carries a generation token so a late
completion cannot overwrite a replacement. Textures, fonts, sheets, scripts,
and scene JSON decode through typed decoders. Nothing pretends browser I/O is
synchronous.
A TextureId is a generation-checked slot handle, so the renderer's texture
registry can release one texture and reuse its slot without a handle nobody
updated drawing whatever lands there next.
AssetLoader drives all of that in one place โ request, enqueue, drain,
decode, apply โ so a caller does not have to know the order. Requesting is
idempotent, a failure is reported once rather than retried forever, and
retain releases what is no longer wanted and says which IDs went, so a host
can drop whatever it built from them. GPU upload stays with the host, which is
the only thing that owns a device.
AssetManifest records what a project ships โ each asset's length and the
SHA-256 of its stored bytes โ as a versioned, ID-ordered file. A loader given
one holds arriving bytes to it, so a truncated response or a stale cache entry
is an error naming the asset rather than a picture from last week; an asset the
manifest does not list still loads. The editor picks one up from the directory
a scene lives in. The generic browser host sizes each kind's bounded request
queue from that manifest, so projects with more than the asset pipeline's
default 64 textures or other same-kind assets still load without increasing
fetch concurrency.
Identical image decoding on every target
Every PNG colour type, 16-bit, interlaced and JPEG images decode pixel-identically natively and on wasm32.
Engine โ ยท Editor โ ยท Decay โ ยท Proof โ
Tested by: crates/sindri-assets/tests/decode_compatibility.rs, crates/sindri-assets/src/decode/tests.rs
Evidence
A corpus of deliberately awkward images โ every PNG colour type, sixteen bits
per channel, an interlaced encoding, and a JPEG โ is decoded and checked pixel
by pixel on both native and wasm32-unknown-unknown, so a texture cannot
decode one way in the editor and another in the browser.
Project fonts
OpenType faces are validated and bound under the scene's logical reference, so no build substitutes an installed font.
Engine โ ยท Editor โ ยท Decay โ ยท Proof โ โ parity row Project fonts
Tested by: crates/sindri-assets/src/decode/tests.rs
Exercised in: games/orbital-baked
Evidence
FontAssetDecoder validates OpenType bytes and records their declared family;
text binds that project-owned face under the scene's logical asset reference,
so native and browser builds never substitute different installed fonts.
Reusable data profiles
Versioned .profile assets hold nested data that many script components read without creating entities.
Engine โ ยท Editor โ ยท Decay โ ยท Proof โ โ parity row Reusable data profiles
Tested by: crates/sindri-decay/tests/a_script_reads_a_profile.rs
Exercised in: games/platformer
See: docs/profiles.md
Evidence
ProfileDocument is the reusable-data counterpart to a prefab: a versioned
.profile asset with an author label, an optional game-defined type, and a
nested JSON-shaped value payload. ProfileAssetDecoder validates it through
the same native/browser loading path, and the manifest records it as
profile. Unlike a prefab it creates no entity; many script components can
hold the same logical profile reference and read one loaded document.
Hot reload
On native, AssetWatch notices a changed file behind a loaded asset and AssetLoader::reload loads it again.
Engine โ ยท Editor ๐ก ยท Decay ๐ก ยท Proof โ โ parity row Hot reload
Tested by: crates/sindri-assets/src/loader/tests.rs
Exercised in: games/platformer
Evidence
On native, AssetWatch notices when the file behind a loaded asset changes and
AssetLoader::reload loads it again, by polling modification time and length
rather than subscribing to filesystem events.