# bsn-smoke — Bevy 0.20 generated-code compile check `bsn-smoke` is Bevy Forge's proof that exported scenes are **real Bevy 0.20 code**, not just plausible-looking text. It is the only crate in this repository that depends on Bevy; it is excluded from the workspace (`Cargo.toml` → `exclude = ["compat/bsn-smoke"]`) so the editor, core and CLI crates stay Bevy-free and build in seconds. ```text compat/bsn-smoke/ ├── Cargo.toml standalone package (empty [workspace]), pinned Bevy 0.20.0 ├── Cargo.lock lockfile for the verification dependency graph ├── src/lib.rs hand-written scenes mirroring every codegen shape + support types ├── src/generated.rs BSN export of the 3D garden demo (written by the gate script) ├── src/generated_2d.rs BSN export of the 2D platformer demo (written by the gate script) └── src/rust_export.rs imperative `Commands::spawn` export shape, also compiled ``` ## What is verified Compile-checked against `bevy 0.20.0` (`default-features = false`): - **Roots:** single root `bsn!` / `-> impl Scene`; multiple roots `bsn_list!` / `-> impl SceneList` with `--` separators. - **Hierarchy:** nested `Children [ ... -- ... ]`, including a `Visibility` patch on a grandchild and components before/after `Children`. - **Names and transforms:** `Name("...")`, full three-field `Transform` with `Quat::from_euler(EulerRot::XYZ, x.to_radians(), ...)`. - **Sprites:** image path handles, `Color::srgba`, `flip_x`/`flip_y`, and braced `custom_size: { Some(Vec2::new(w, h)) }` (the brace form is mandatory in BSN patches). - **Text:** `Text2d`, `TextFont { font_size: FontSize::Px(..) }`, `TextColor`. - **Cameras:** bare `Camera2d`, `Camera3d::default()`. - **Lights:** `PointLight` (color/intensity/range/shadows) and `DirectionalLight` (color/illuminance/shadows). - **Custom components:** struct-with-fields patches (`Health { current: 10.0, max: 10.0 }`, `forge_demo::Orbit { radius: 2.4, speed: 0.45 }`) and bare unit-component patches (`Marker`). - **3D meshes:** all five primitive shapes (`Cuboid`, `Sphere`, `Plane3d`, `Cylinder`, `Capsule3d`) with braced method-call handles (`Mesh3d({ meshes.add(..) })`) and inline materials (`MeshMaterial3d::({ materials.add(StandardMaterial { ..default() }) })`), plus an asset mesh path (`Mesh3d("models/...#Mesh0/Primitive0")`) with an inline material. - **Generated signatures:** mesh scenes take `&mut Assets` and `&mut Assets` only when needed. - **Imperative Rust export shape** (`src/rust_export.rs`): `commands.spawn`, `with_children`, `Name::new`, `Transform::from_xyz(..).with_rotation(..).with_scale(..)`, `Sprite { ..default() }`, `Mesh3d`/`MeshMaterial3d`, `Text2d::new`, `TextFont`, `TextColor`, `Camera3d::default()`, lights and `Visibility`. The committed `src/generated*.rs` files are the CLI exports produced by the last successful gate run (plus the shim described below), so the crate compiles on a fresh checkout even before the script runs. `scripts/check-generated-code.sh` regenerates them from the demo project and recompiles, verifying the generator + demo scenes end to end. The exports are deterministic, so re-running the gate leaves the files unchanged. ### Compatibility shim for custom type paths Generated BSN references custom components by their project type path, e.g. `forge_demo::Orbit`. In a real Bevy project the generated function is pasted where that path is already in scope. Here the generated file is a module, so the gate script prepends four lines (`#![allow(unused_imports)]`, two comments, and `use crate::*;`) to expose the crate-root support types; **the generated code itself is appended unchanged**. `lib.rs` provides `forge_demo::Orbit` with the same fields as `examples/demo-project/src/main.rs`. If a demo scene introduces another custom type, add its support type to `lib.rs`. ## Bevy version and feature set Pinned to **Bevy 0.20.0** through `Cargo.lock`. Feature set (chosen by `.swarm/recon/R02.md` to keep compile time and APIs aligned with the verified BSN forms): ```toml bevy = { version = "0.20", default-features = false, features = [ "std", "bevy_scene", "bevy_sprite", "bevy_asset", "bevy_color", "bevy_text", "bevy_pbr", "bevy_mesh", "bevy_camera", "bevy_light", "bevy_image", ] } ``` No windowing or audio backends are compiled in and no GPU device is initialized: this is a **compile** check that needs no display, asset files or `App`. ## Running Directly (the load-bearing check): ```bash cd compat/bsn-smoke CARGO_NET_OFFLINE=true cargo check ``` End to end (generator → CLI → Bevy compile): ```bash bash scripts/check-generated-code.sh ``` The script exports `examples/demo-project/scenes/garden.forge.ron` and `.../platformer.forge.ron` with `cargo run -q -p bevy_forge_cli -- export ... --format bsn`, writes the results into this crate, and runs the check. It fails loudly on any error; if the CLI or demo project does not exist yet it fails with a clear message instead of silently passing. `FORGE_SKIP_GENERATED=1` skips with an explicit message (bootstrap only); `FORGE_SKIP_SECOND_SCENE=1` skips the 2D export; `FORGE_GARDEN_SCENE` / `FORGE_PLATFORMER_SCENE` override the scene paths; `CARGO_TARGET_DIR` is passed through. ## Expected cost Measured on this Linux machine (Rust 1.99.1, 2026-10): | Build | Wall time | `target/` size | |---|---|---| | First `cargo check`, cold target dir | **2 min 15 s** | ~803 MB | | Subsequent `cargo check`, warm target dir | < 1 s | — | | Reusing an existing Bevy 0.20 `target/` (e.g. recon) | ~1 s | — | `compat/bsn-smoke/target/` is ignored by `.gitignore`; never commit it. If you already have a warm Bevy 0.20 target directory, point `CARGO_TARGET_DIR` at it to avoid the cold build (the dependency feature set must match exactly). ## Runtime caveats (compile ≠ loaded assets) - Asset **paths** (`Sprite { image: "textures/x.png" }`, `Mesh3d("models/...gltf#...")`) compile, but Bevy turns them into `HandleTemplate::Path` values that are resolved by the `AssetServer` at spawn time. If the project has no loader/asset for the path, the handle stays loading/failed and nothing renders — it is not a compile error (see R02 §9). - Bevy Forge never emits material **asset paths** (`MeshMaterial3d::("...")`), because those need a project-registered material loader; materials are always inline `materials.add(StandardMaterial { ..default() })` handles. - The check is compile-only: it does not spawn the scenes or validate asset existence. Asset references are validated separately by `forge validate` / the editor.