Blacksite/docs/adr/0045-content-workspace-and-material-fallback-contract.md

7.5 KiB

ADR 0045: Content Workspace and Material Fallback Contract

Status

Accepted

Context

Blacksite's Content Browser scans the project assets/ tree, but authored RON type detection and import destinations still depend on conventional folders. The asset registry is editor-owned and serialized as an unversioned record list, so validation, packaging, runtime hydration, and future headless processing duplicate parts of its contract. Imported model materials are either read-only source defaults or scene-level assignments; a model asset cannot own editable project defaults.

Missing renderer materials currently create separate magenta StandardMaterial values, while skinned, primitive, brush, terrain, thumbnail, raster, and Solari paths use different fallback rules. A missing or broken material therefore does not have one predictable, production-safe result.

Decision

  • User-authored content may live in any normal nested folder beneath the project assets/ root. Folder names are organizational only. External absolute linked assets are not supported.

  • AssetId is authoritative identity. Project-relative paths and labels are repairable cached metadata. Moves and reimports reconcile by stable IDs and never by display name.

  • Registry contracts move to shared::content: typed asset kinds, versioned registry documents, project content defaults, import settings, model material policies, and runtime catalog records. UI-independent filesystem scanning, classification, transaction planning, import/reimport, and processing live in a content_pipeline crate used by the editor, validator, watcher, and xtask.

  • Registry schema v3 is AssetRegistryDocument { schema_version, defaults, records } with typed Model/Texture import settings as extended by ADR 0046. The source registry remains editor-managed under assets/.index/; packages receive a stripped runtime catalog without editor import provenance or source-only dependencies.

  • Project content roots normalize to assets/. .index, .trash, thumbnails, recovery data, and implementation-owned derived artifacts are hidden and protected from normal file operations.

  • Fixed-folder consumers are retired or explicitly constrained as follows:

    Consumer Current contract / migration
    Project settings and templates asset_roots upgrades to exactly ["assets"]; the explicit project upgrader performs the write.
    Content Browser and registry scanner Walk all user-managed descendants and classify by extension plus document schema, never by parent folder.
    Material, shader, model, audio, level, and prefab lookup Resolve the registry's stable ID to its current path; a cached path is a repair hint only.
    Scene/prefab validation and hydration Consume registry/runtime-catalog resolution rather than conventional authoring directories.
    Packaging and headless processing Scan the same content root and publish the same stripped catalog and normalized model manifests.
    Material creation UX The current browser folder is authoritative; assets/materials/ remains only a Material Library convenience default.
    Generated mesh, animation, navigation, thumbnail, trash, and index data Their fixed implementation-owned locations remain hidden managed storage, excluded from user classification and ordinary file operations.
  • Model material selections are per stable slot: Source, Project Material/Instance, or Default. Extracting a source material creates an ordinary project Material and then stores a Project selection. Re-extraction may replace a provenance-matched Material only after an explicit diff and fingerprint-guarded Apply decision; Create New remains available and unrelated provenance is never overwritten. Reimport retains removed project assignments as explicit orphans.

  • Effective material precedence, strongest first, is: runtime MaterialPropertyBlock, scene/prefab assignment, model-asset default, imported source, project default, built-in DefaultGrid. Missing layers inherit downward. A configured but broken authored reference renders the fallback and reports the broken layer rather than silently revealing a different authored layer.

  • Primitive surfaces participate in that same chain through their saved slot:primitive:surface; they no longer hydrate from a separate actor-local descriptor. Static and skinned draws bind their exact saved slot IDs through the same runtime binding contract.

  • DefaultGrid is immutable engine content compiled into the renderer. It is a shared, UV-independent world-space checker with raster/Solari parity and a neutral StandardMaterial emergency path. A project may choose a Material/Instance fallback; clearing or breaking it returns to DefaultGrid. Terrain retains its specialized layer fallback.

  • Built-in DefaultGrid is not editable, instanceable, or a valid MaterialPropertyBlock promotion base. Promotion requires a resolved project or imported Material base.

  • Ordinary editor/runtime loading is read-only. Registry, project-settings, and scene migrations run only through the explicit transactional project upgrader.

  • Editor thumbnails use typed content-addressed artifacts under managed assets/.thumbnails/<version>/ storage. Their visual signatures cover source/dependency bytes, resolved Material inputs and textures, custom shader files, subasset identity, and the studio renderer version. Normal catalog invalidation revalidates the signature while retaining the last-good GPU image; replacement publishes atomically after a valid render/readback. These artifacts are editor-only, untracked, watcher-excluded, and absent from packages.

Consequences

  • Editor, runtime, validation, packaging, watcher, and headless processing share one content model and one stable-ID resolver.
  • Arbitrary organization no longer changes asset type, runtime behavior, or package inclusion.
  • The content pipeline becomes a reusable crate boundary instead of an editor implementation detail; this adds one workspace crate and moves registry types out of editor.
  • Older registries, conventional asset roots, global model material policy, and legacy mesh material overrides require the explicit transactional upgrader; normal loading accepts current schemas.
  • Broken assignments stay visible and diagnosable, but may look different from their previous imported source because failure uses the project/engine fallback deliberately.
  • One cached DefaultGrid/emergency handle covers repeated primitive and mesh hydration, so missing assignments do not grow the material asset stores.
  • Thumbnail cache ownership includes the backing Egui image registration. Replacement releases the superseded registration only after the new artifact is usable; manual retry/full teardown release it immediately. Catalog refresh retains it as last-good while revalidating visual signatures, preventing both refresh flicker and hidden GPU resource growth.
  • File operations and imports must be staged transactions that update authored references, registry state, derived manifests, and runtime catalogs together.
  • Ordinary asset-property edits use the explicit dirty-document and two-phase publication boundary defined by ADR 0047; topology transactions remain immediate.
  • Scene schema v6 carries actor/model/imported/inherit origin explicitly at hydration time, so a broken authored layer uses DefaultGrid instead of accidentally revealing a weaker layer.