scena 1.0.0

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
# Module Boundary Contract

Type: Contract.

**Authority.** This document is an implementation contract. The RFC is the canonical
narrative; this contract supersedes the RFC for module ownership rules. Accepted ADRs in
`docs/decisions/` override both.

This document defines implementation ownership. Public examples may re-export simple types,
but code should keep these module responsibilities separate.

## Ownership Table

| Module | Owns | Must Not Own |
|---|---|---|
| `scene` | graph storage, typed keys, transforms, bounds, cameras, lights, anchors, clipping, scene queries, `scene::interaction` hover/selection state | GPU device resources, asset fetch, platform events |
| `assets` | fetchers, caches, glTF/GLB parsing, parsed clip data, texture decoding, asset handles, retain policy, hot reload | scene graph mutation, GPU resources, rendering passes, window/canvas state |
| `geometry` | primitive builders, generated meshes, technical lines, grids, axes, labels metadata | material shading policy, GPU upload, scene hierarchy |
| `material` | material descriptors, texture slots, color-space tags, alpha modes, PBR/unlit configuration | texture fetching, GPU pipeline creation, renderer stats |
| `render` | wgpu device/surface, prepare lifecycle, pipelines, passes, render targets, GPU resource table, stats | asset fetching/parsing, application event loop, domain state |
| `animation` | runtime mixer state, rebound animation channels, skinning palette state, morph target playback | parsed asset cache ownership, physics, simulation, game update loop, host frame scheduling |
| `controls` | platform-neutral camera control math and optional event adapters | global input dispatch, app event ownership, renderer internals |
| `picking` | ray construction, bounds/BVH/triangle tests, typed hit results | selection state owned by the application, domain actions |
| `diagnostics` | structured errors, warnings, capability reports, debug overlay data | swallowing errors, implicit fallbacks without reporting |
| `platform` | thin winit/browser/headless adapters | renderer logic, asset ownership, scene mutation rules |
| `testing` | headless helpers, screenshot/pixel comparisons, leak/allocation harnesses | production renderer state hidden from normal APIs |

## Boundary Rules

- `Renderer` consumes prepared scene/resource state. It does not fetch, parse, or own source
  assets.
- `Assets` may decode and retain CPU-side source data, but it does not add nodes to a
  `Scene`.
- `Scene` owns graph identity and mutation. It never stores raw GPU objects.
- `platform` modules translate host events and surfaces into renderer APIs. They do not
  contain render-pass, asset-cache, or scene-graph logic.
- `diagnostics` must make fallback decisions visible through structured warnings or errors.
- `controls` may compute camera transforms from host events, but the host still owns the
  event loop and decides when to call `render()`.
- `picking` returns typed hits. `scene::interaction` stores only renderer-visible hover and
  primary selection state; application semantic actions remain outside the renderer.
- Public convenience APIs may compose modules, but the underlying ownership stays explicit.

## SOLID/KISS Gate

Every public feature must name exactly one owner module, its supporting modules, its
structured error surface, and its proof surface before implementation starts.

KISS constraints:

- No catch-all `Manager`, `Engine`, `World`, or broad `Context` type in v1.0 implementation.
  Narrow context values are allowed only when their owner is explicit, for example
  `InteractionContext`, `RenderContext`, `PrepareContext`, or `DiagnosticContext`.
- No global singleton renderer, asset manager, event loop, or mutable registry.
- No abstraction is added only for future flexibility. Add an abstraction when it removes
  real duplication, narrows ownership, or makes an existing contract easier to enforce.
- Source modules crossing 500 significant non-comment lines must be split or justified by an
  ADR-backed doctor allowlist before the owning milestone exits.
- Public convenience APIs may be ergonomic, but they must delegate to the same owner module
  contracts as the lower-level API.

Host-owned convenience facade exceptions:

This section is the host-owned convenience facade exceptions contract for v1.0.

- `HeadlessGltfViewer` and `InteractiveGltfViewer` are the v1.0 host-owned convenience
  facade exceptions to the "no public type may become a catch-all facade" warning. They
  compose the first headless/model-viewer flows; they are not new owner modules.
- These viewers may hold `Assets`, `Scene`, `Renderer`, `SceneImport`, camera, and optional
  controls only so the host can call load -> instantiate -> frame -> prepare -> render in
  one object.
- The viewers must not expose owner-module fields directly, own the application event loop,
  hide asset fetches in `render`, hide GPU upload in `render`, or bypass the lower-level
  `Scene`, `Assets`, and `Renderer` contracts.
- Mutable accessors remain explicit escape hatches back to the owner modules.

Large module allowlist:

| Module | Owner | Justification |
|---|---|---|
| `src/assets.rs` | `assets` | Public asset store, cache, retained reload, and typed handle facade; split submodules own environment, glTF, load reporting, fetchers, and texture decoding. |
| `src/assets/environment.rs` | `assets` | Environment descriptor and fixture metadata stay together to keep cache identity and derivative metadata auditable. |
| `src/viewer.rs` | host convenience facade | v1.0 first-render helper intentionally composes owner modules without owning the event loop or renderer internals. |
| `src/material.rs` | `material` | Public immutable material descriptors and texture-slot helpers remain one user-facing value surface for API stability. |
| `src/picking.rs` | `picking` | Ray construction, hit sorting, and typed hit payloads share one narrow interaction contract. |
| `src/scene/inspection.rs` | `scene` | Inspection reports are grouped so debug metadata mirrors the scene graph without adding serialization ownership. |
| `src/render/prepare/resources.rs` | `render` | Prepare resource extraction is centralized because it is the single bridge from scene/assets state to renderer-owned prepared data. |
| `src/assets/gltf.rs` | `assets` | Top-level glTF `SceneAsset` public types and parser orchestration stay together while specialized parsing lives in child modules. |

SOLID constraints:

- Single responsibility: each module owns only the responsibilities listed in the ownership
  table.
- Open/closed: v1.0 keeps the fixed render graph and reserves v1.x extension points without
  exposing unstable custom-pass APIs.
- Liskov/interface substitution: backend degradation is represented through capabilities,
  diagnostics, and structured errors, not backend-specific public object types.
- Interface segregation: `Scene`, `Assets`, `Renderer`, and `SceneImport` remain separate
  public nouns; no public type may become a catch-all facade for all four.
- Dependency inversion: platform adapters translate host events into renderer APIs. Renderer
  internals do not depend on winit, DOM, browser event objects, or application event loops.

## Forbidden Crossings

- No hidden asset fetch, shader compile, or first-time GPU upload inside `render()`.
- No `Scene` mutation from `Renderer::render()`.
- No direct backend objects exposed as public handles.
- No silent fallback when a required backend capability or required glTF extension is
  missing.
- No global singleton renderer, asset manager, input dispatcher, or event loop.
- No simulation, robotics, PLC/domain, process, physics, or game-engine concepts in any
  module.

## Review Gate

Every new public feature must name:

- its owner module;
- any supporting module it depends on;
- its structured error surface;
- its proof surface: unit, integration, headless screenshot, browser rendered-output,
  capability, leak, allocation, or benchmark test.