Blacksite/docs/adr/0029-validation-gated-build-profiles-and-packaging.md
Rbanh 0553a85220
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
Build production-ready editor authoring workflows
2026-07-11 12:41:04 -04:00

4.5 KiB

ADR 0029: Validation-gated build profiles and packaging

Status

Accepted

Context

Blacksite builds previously required users to know Cargo commands and manually assemble assets. That made it easy to ship broken references, editor caches, recovery data, or an executable without the settings and content that produced it. Ordinary debug binaries are also too large for practical distribution, while release LTO is too expensive for every development package.

Decision

Projects define versioned RON profiles under assets/build_profiles/. A profile selects the Cargo package profile, optional target triple and feature set, rendering tier, default scene, and a safe project-relative output directory. Development and QA exports use dedicated optimized non-LTO Cargo profiles; QA retains line tables, while release exports retain thin LTO and strip symbols. The development rendering tier forces the portable Forward GI path in the packaged project settings; representative and shipping tiers preserve the authored rendering policy. Windows MSVC QA packages include Cargo's PDB sidecar and record it in the content manifest.

cargo package-project is the authoritative build/package entry point for editor and CI. It runs scene::validate_project before compiling, builds game, reads the executable path from Cargo's JSON artifact stream, then validates again against a BLAKE3 snapshot of the runtime inputs. A profile default scene must be a regular .scn.ron file under the fully audited assets/levels/ tree and pass schema plus composition validation. The selected default scene and rendering tier are applied to the packaged project.ron. Metadata records profile, target, features, rendering tier, default scene, exact engine/project commit and dirty state, Rust/Cargo versions, and a sorted BLAKE3 content manifest. Features that require the development or hot-reload dynamic libraries are rejected because those libraries are not shipping artifacts.

Runtime packaging excludes the editor asset registry, trash, thumbnails, build profiles, recovery and autosave data, temporary/backup files, and source-authoring formats. Traversal errors and runtime asset symlinks are blocking instead of silently omitting content. Output is restricted to a dedicated non-symlinked dist/<name>/ tree. A non-empty existing directory must carry recognized package metadata before it can be seeded or pruned, preventing a profile typo from replacing project source or an unrelated user directory.

Assembly happens in a unique sibling staging directory copied from the previous package. Staging has no shared file inodes with the published output, and its complete content is rehashed before promotion, so a failed build or external output edit cannot mutate the candidate package. An OS-backed per-output lock prevents concurrent publication on Unix and Windows. Versioned ownership records authenticate lock/staging cleanup; an interrupted run removes only owned partial staging and restores a strictly validated prior backup before retrying. Only a complete, unchanged-input package is promoted; stale files and obsolete directory shapes are pruned in staging. The editor Build panel invokes the same engine-workspace Cargo alias for the active external project, streams stdout/stderr, owns and cancels its worker/process tree, and exposes Build, Build and Run, and Reveal Output. Dirty scene tabs block a build. Cross-target packages are produced but Build and Run refuses to execute a binary for a different host operating system or CPU architecture.

Consequences

  • Editor and headless builds have identical validation, profile, inclusion, and metadata semantics.
  • Blocking content errors fail before compiler or output work and are checked again after a long compile. Concurrent asset mutation aborts staging without replacing the previous package.
  • The first build of a package profile compiles optimized intermediates; later unchanged builds reuse Cargo intermediates and avoid rewriting byte-identical files after seeding the staged copy.
  • Cross-compilation still requires the selected Rust target and platform linker/toolchain to be installed; missing tools are reported by Cargo in the Build panel. The native layout currently accepts desktop Linux and Windows targets only; web, mobile, and platform-bundle formats require dedicated future packagers rather than being mislabeled as ready native packages.
  • --skip-build requires an explicit matching game artifact, avoiding guesses about Cargo target directories or configured default targets.