# 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](0046-schema-driven-material-inputs-and-processed-textures.md). 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.

## 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, retry, and
  catalog invalidation explicitly release the last registration before a new studio render owns
  the key, preventing refresh-driven 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](0047-editor-authored-asset-documents.md); 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.
