Forge — Export & Engine Import
Export is the egress boundary of every Forge pipeline. A wrong format, flipped axis, or
silent 100× scale error renders wrong and fails visual QA. This skill owns the format decision,
the export command, the post-export validation, and the per-engine import recipe.
Project memory: if FORGE.md exists at the project root, read it first — it carries
the target engine, coordinate system (Y-up/Z-up), scale unit, PBR workflow, and output paths
that gate every decision below.
Windows headless truths: EEVEE-Next is unsupported headless on Windows — use Cycles for
any render-verify step. Blender CLI: blender -b scene.blend -P script.py -- <args> (the --
separator is mandatory). Call openscad.com not .exe. Use python not python3. Absolute
forward-slash paths in Blender filepath (never //). --python-exit-code 1 so errors fail
the process.
Forge suite — this skill's position:
| Upstream (produces source) |
forge-export |
Downstream (consumes output) |
| forge-model, forge-parametric, forge-procedural → geometry |
→ validates + exports |
forge-optimize → web delivery |
| forge-material, forge-texture, forge-uv → look-dev |
→ bundles materials |
atelier-webgl → Three.js/R3F scene |
| forge-rig, forge-animate, forge-sim → motion |
→ carries animation |
Unreal / Unity / Godot runtime |
| forge-validate (called here) |
→ gates every export |
forge-render (verify PNG) |
Run = call the Skill tool. Writing "next: forge-validate" in prose runs nothing.
Every cross-skill handoff MUST be Skill("forge-validate"), Skill("forge-optimize"), etc.
Decide first: pick the export format + tool
Before executing any export command, resolve three questions from FORGE.md (or ask explicitly):
- Target delivery — web/Three.js, Unreal, Unity, Godot, AR/USDZ, print/STL, multi-DCC
pipeline? See the format matrix below.
- Source type —
.blend, .usdc, .fbx, procedural bpy, OpenSCAD .stl?
- Tool availability — verify Blender is on
PATH before any Blender export. For USD tools
verify usdchecker; for Assimp CLI verify assimp version.
Format decision matrix (quick reference):
| Target |
Recommended format |
Primary tool |
| Web / Three.js / R3F / atelier-webgl |
GLB (binary glTF 2.0) |
Blender bpy → forge-optimize |
| Unreal Engine 5.x |
FBX 2020 or GLB (Interchange) |
Blender bpy |
| Unity 6 / URP / HDRP |
FBX (AssetPostprocessor) or GLB (glTFast) |
Blender bpy |
| Godot 4.3+ |
GLB (headless --import) |
Blender bpy |
| iOS AR / Quick Look |
USDZ |
usdzip --arkitAsset |
| Multi-DCC pipeline hub |
USDC (binary crate) |
usd-core pxr |
| Baked sim cache |
Alembic (.abc) |
Blender wm.alembic_export |
| 3D print |
STL or 3MF |
Blender or assimp |
| CAD ingress |
STEP → tessellate → GLB |
FreeCAD CLI → assimp |
| Batch format conversion |
assimp CLI (glb2 format ID) |
assimp export |
Full command-level detail: see references/format-matrix.md and references/blender-export.md.
The flow
1. Read FORGE.md — load target engine, coordinate system, poly budget, output paths.
If absent, prompt for target engine and scale unit before continuing.
2. Decide format + verify tool (the gate — do not skip):
blender --version must succeed before any Blender export.
python -c "from pxr import Usd" before any USD authoring.
assimp version before any assimp batch conversion.
- If a required tool is missing, surface the install path and halt. Per-tool install blocks:
assimp →
references/format-matrix.md §4; glTF-Validator + gltf-transform →
references/gltf-validator.md §1/§7; USD (usd-core / NVIDIA prebuilt) →
references/usd-pipeline.md §1; web-optimizer tools (KTX/gltfpack) live downstream in
forge-optimize's references/install-windows.md. Blender: install Blender 4.2 LTS to
C:\Program Files\Blender Foundation\Blender 4.2\ and add it to PATH (see
references/blender-export.md §1 for the canonical binary path).
3. Pre-export prep (do only what the format needs):
- glTF/GLB: confirm all materials are PBR metallic-roughness, not Principled BSDF with
unsupported nodes. Non-color textures (ORM, normal) must be in Linear color space in Blender.
- FBX → Unreal: confirm
axis_forward='-Z', axis_up='Y'; apply_unit_scale=True.
- USD: set
defaultPrim, upAxis=Y, metersPerUnit. Confirm all meshes have
subdivisionScheme=none for hard-surface.
- USDZ: geometry must be triangulated; textures PNG or JPEG only; no absolute file paths.
4. Export — choose the right bpy operator or CLI tool:
- Blender GLB:
bpy.ops.export_scene.gltf(filepath=…, export_format='GLB', export_yup=True, export_apply=False)
Full parameter tables: references/blender-export.md.
- Blender FBX:
bpy.ops.export_scene.fbx(filepath=…, axis_forward='-Z', axis_up='Y', apply_unit_scale=True)
- USD:
bpy.ops.wm.usd_export(filepath=…, generate_preview_surface=True, rename_uvmaps=True)
- Assimp batch:
assimp export "in.fbx" "out.glb" -fglb2 -tri -jiv -gsn -cts -fuv -icl
- USDZ:
usdzip --arkitAsset scene.usda out.usdz then usdchecker --arkit --strict out.usdz
- All scripts go in
.forge-build/out/ working dir; web handoff to public/forge/<slug>-hero.glb.
5. Validate — non-negotiable gate:
- GLB:
gltf_validator.exe --stdout out.glb — zero errors required before proceeding.
- USDZ:
usdchecker --arkit --strict out.usdz.
- Degrade, never skip: if
gltf_validator/usdchecker is absent, fall back to
gltf-transform validate out.glb (Khronos spec; install per references/gltf-validator.md
§7) and note the substitution in FORGE.md. Validation is never silently skipped.
- Invoke
Skill("forge-validate") for the full manifold/normals/poly-budget/render gate.
6. Verify by render — load the export into a FRESH scene and render one deterministic Cycles
frame (EEVEE-Next is unsupported headless on Windows). Full copy-paste script + invocation:
references/render-verify.md (mirrors forge-sim/export-cache.md §8).
blender -b --python-exit-code 1 -P render_verify.py -- --in out.glb --out verify.png
The script forces render.engine='CYCLES', cycles.device='CPU', a single fixed frame,
samples=32, seed=0 so re-verifies reproduce. Guard before Read: confirm verify.png
exists and is > 1 KB (the script exits non-zero otherwise). Then Read("verify.png") to
inspect visually. A missing / < 1 KB / all-black PNG = silent export failure (no geometry,
dead material, or 100x scale) — fix the export and re-verify, not the verify script.
7. Hand off (if web delivery):
- Run
Skill("forge-optimize") → Draco+Meshopt+KTX2 → public/forge/<slug>-hero.glb.
forge-optimize owns the final Skill("atelier-webgl") call that wires the asset into the
Three.js/R3F scene — export produces and validates the GLB, then hands it down the chain.
- Naming contract:
public/forge/<slug>-hero.glb + <slug>-hero-poster.webp.
- Poster (reduced-motion fallback) must be rendered BEFORE the GLB handoff.
- Boundary: Forge's asset gate is
forge-validate; the WEB-runtime gate
(CWV/LCP/CLS/INP, DOM a11y / alt text) is atelier-perf-a11y's job, run downstream after
Skill("atelier-webgl") — do not duplicate or skip it here.
Engine import recipes
Unreal Engine 5.x
Full detail: references/engine-unreal.md
- 1 UU = 1 cm. Always set
convert_scene_unit=True at import or export at cm scale from Blender
(apply_unit_scale=True).
- Z-up left-handed. Enable
convert_scene=True at import (handles Blender's Z-up/RH).
- Interchange API (UE 5.4+) is preferred for GLB; legacy
FbxImportUI still works for FBX.
- Nanite: enable at import for meshes > 10k tris, Opaque/Masked only — NOT translucent,
NOT morph targets, NOT mobile targets.
- FBX naming: collision hulls
UCX_MeshName_00, LOD suffixes _LOD0…_LOD3 in the same FBX.
- Headless import:
UnrealEditor-Cmd.exe project.uproject -run=pythonscript -script=import.py -unattended -nullrhi
- sRGB flags: BaseColor/Emissive ON; Normal/ORM/Roughness/Metallic/AO OFF — the #1 cause of
wrong materials on import.
Unity 6 (URP / HDRP)
Full detail: references/engine-unity.md
- 1 unit = 1 meter. Enable
useFileUnits=true + bakeAxisConversion=true in ModelImporter.
bakeAxisConversion bakes the axis fix into vertex data (not a root Transform rotation), which
avoids breaking physics raycasts and NavMesh.
- ORM channel layout differs per pipeline:
- URP Metallic Map: R=Metallic, G=AO, B=unused, A=Smoothness (1−Roughness).
- HDRP Mask Map: R=Metallic, G=AO, B=DetailMask, A=Smoothness.
- glTF spec: R=AO, G=Roughness, B=Metallic. Interchange auto-remaps; FBX does not.
- glTFast (
com.unity.cloud.gltfast 6.17) is the recommended runtime/editor glTF importer.
- Headless import:
Unity.exe -projectPath … -batchmode -quit -executeMethod MyClass.Method
- AssetPostprocessor
GetVersion() must be incremented on every logic change or reimport won't fire.
Godot 4.3+
Full detail: references/engine-godot.md
- Prefer GLB (single file, no sidecar). Commit
.import files; gitignore .godot/imported/.
- Headless import:
godot.exe --headless --import --path C:\Project (4.3+ flag; blocks until done).
- Known bug (4.4.x):
ERROR: Parameter "t" is null during headless glTF import — non-fatal,
import still completes. Fixed in 4.6 / 4.5.x (PR #109116).
- ORM: glTF spec layout (R=AO, G=Roughness, B=Metallic). ORM textures must import as linear
(
flags/srgb=false in the .import file).
- Collision suffixes on mesh node names:
-col (trimesh+visible), -colonly (trimesh+invisible),
-convcol (convex+visible), -convcolonly (convex+invisible). Note: suffix applies to the node
name, not the mesh data name (open bug #115869 as of 2026).
- LOD: set
meshes/generate_lods=true in .import (uses meshoptimizer, zero runtime cost).
- Normal maps: Godot uses OpenGL convention (Y+ = up). Substance Painter default is OpenGL.
DirectX normals (Y+ = down) must have the green channel inverted before import.
Operating principles
- Format before execution. Pick the right format for the target — then choose the right tool.
GLB is the runtime hub; USDC is the pipeline hub; FBX is the DCC-to-engine legacy path.
Never default to FBX when the target accepts GLB.
- Gate at validation, not at guesswork. Run
gltf_validator (zero errors) and
Skill("forge-validate") before declaring an export done. A file that passes the validator
but renders blank has a material or lighting problem — verify by render.
- Coordinate system is a first-class concern. glTF = Y-up right-handed meters. Unreal = Z-up
left-handed cm. Unity = Y-up left-handed meters. Godot = Y-up right-handed meters. Every
export command must address the axis and unit explicitly — never rely on defaults.
- Scripts, not inline code. The
blender -b -P script.py -- args invocation pattern lives in
a file; do not paste multi-line Python into --python-expr for anything beyond trivial one-liners
(the model pre-evaluates $() in SKILL.md code blocks — real logic belongs in scripts).
- Run = call the Skill tool. Saying "now run forge-optimize" in prose runs nothing.
This skill's direct handoffs are
Skill("forge-validate") (the gate) and Skill("forge-optimize")
(web delivery); forge-optimize makes the final Skill("atelier-webgl") call — do not call it here.
1---2name: forge-export3description: Forge suite — format matrix, glTF/GLB/USD/FBX export, and engine import conventions. Produces a validated, engine-ready asset (GLB, USDC, FBX, or USDZ) from any Forge working file. Use whenever: exporting a Blender scene or procedural mesh to glTF/GLB; converting FBX, OBJ, or STL to GLB; building a USD stage for multi-DCC pipeline; packaging USDZ for iOS AR / Quick Look; importing a GLB into Unreal Engine (Interchange, Nanite, FBX naming), Unity (FBX/glTFast, ORM pack, axis bake), or Godot 4 (headless --import, sidecar .import, collision suffixes); fixing 100× FBX scale; choosing between glTF, USD, FBX, OBJ, Alembic, STEP for a given pipeline stage; or producing a web-ready GLB and handing it to forge-optimize for the atelier-webgl handoff (forge-optimize owns the final Skill(atelier-webgl) call). HEADLESS-ONLY: driven from code, output verified by reading a PNG. Part of the Forge suite.4---56# Forge — Export & Engine Import78Export is the **egress boundary** of every Forge pipeline. A wrong format, flipped axis, or9silent 100× scale error renders wrong and fails visual QA. This skill owns the format decision,10the export command, the post-export validation, and the per-engine import recipe.1112> **Project memory:** if **`FORGE.md`** exists at the project root, read it first — it carries13> the target engine, coordinate system (Y-up/Z-up), scale unit, PBR workflow, and output paths14> that gate every decision below.15>16> **Windows headless truths:** EEVEE-Next is unsupported headless on Windows — use **Cycles** for17> any render-verify step. Blender CLI: `blender -b scene.blend -P script.py -- <args>` (the `--`18> separator is mandatory). Call `openscad.com` not `.exe`. Use `python` not `python3`. Absolute19> forward-slash paths in Blender `filepath` (never `//`). `--python-exit-code 1` so errors fail20> the process.2122---2324> **Forge suite — this skill's position:**25>26> | Upstream (produces source) | **forge-export** | Downstream (consumes output) |27> |---|---|---|28> | **forge-model**, **forge-parametric**, **forge-procedural** → geometry | → validates + exports | **forge-optimize** → web delivery |29> | **forge-material**, **forge-texture**, **forge-uv** → look-dev | → bundles materials | **atelier-webgl** → Three.js/R3F scene |30> | **forge-rig**, **forge-animate**, **forge-sim** → motion | → carries animation | Unreal / Unity / Godot runtime |31> | **forge-validate** (called here) | → gates every export | **forge-render** (verify PNG) |32>33> **Run = call the Skill tool.** Writing "next: forge-validate" in prose runs nothing.34> Every cross-skill handoff MUST be `Skill("forge-validate")`, `Skill("forge-optimize")`, etc.3536---3738## Decide first: pick the export format + tool3940Before executing any export command, resolve three questions from FORGE.md (or ask explicitly):41421. **Target delivery** — web/Three.js, Unreal, Unity, Godot, AR/USDZ, print/STL, multi-DCC43 pipeline? See the format matrix below.442. **Source type** — `.blend`, `.usdc`, `.fbx`, procedural bpy, OpenSCAD `.stl`?453. **Tool availability** — verify Blender is on `PATH` before any Blender export. For USD tools46 verify `usdchecker`; for Assimp CLI verify `assimp version`.4748**Format decision matrix (quick reference):**4950| Target | Recommended format | Primary tool |51|---|---|---|52| Web / Three.js / R3F / atelier-webgl | **GLB** (binary glTF 2.0) | Blender bpy → forge-optimize |53| Unreal Engine 5.x | **FBX 2020** or **GLB** (Interchange) | Blender bpy |54| Unity 6 / URP / HDRP | **FBX** (AssetPostprocessor) or **GLB** (glTFast) | Blender bpy |55| Godot 4.3+ | **GLB** (headless `--import`) | Blender bpy |56| iOS AR / Quick Look | **USDZ** | `usdzip --arkitAsset` |57| Multi-DCC pipeline hub | **USDC** (binary crate) | `usd-core` pxr |58| Baked sim cache | **Alembic** (`.abc`) | Blender `wm.alembic_export` |59| 3D print | **STL** or **3MF** | Blender or assimp |60| CAD ingress | **STEP** → tessellate → GLB | FreeCAD CLI → assimp |61| Batch format conversion | assimp CLI (`glb2` format ID) | `assimp export` |6263Full command-level detail: see **`references/format-matrix.md`** and **`references/blender-export.md`**.6465---6667## The flow6869**1. Read FORGE.md** — load target engine, coordinate system, poly budget, output paths.70If absent, prompt for target engine and scale unit before continuing.7172**2. Decide format + verify tool** (the gate — do not skip):73 - `blender --version` must succeed before any Blender export.74 - `python -c "from pxr import Usd"` before any USD authoring.75 - `assimp version` before any assimp batch conversion.76 - If a required tool is missing, surface the install path and halt. Per-tool install blocks:77 assimp → **`references/format-matrix.md`** §4; glTF-Validator + gltf-transform →78 **`references/gltf-validator.md`** §1/§7; USD (`usd-core` / NVIDIA prebuilt) →79 **`references/usd-pipeline.md`** §1; web-optimizer tools (KTX/gltfpack) live downstream in80 `forge-optimize`'s `references/install-windows.md`. Blender: install Blender 4.2 LTS to81 `C:\Program Files\Blender Foundation\Blender 4.2\` and add it to `PATH` (see82 `references/blender-export.md` §1 for the canonical binary path).8384**3. Pre-export prep** (do only what the format needs):85 - glTF/GLB: confirm all materials are PBR metallic-roughness, not Principled BSDF with86 unsupported nodes. Non-color textures (ORM, normal) must be in Linear color space in Blender.87 - FBX → Unreal: confirm `axis_forward='-Z'`, `axis_up='Y'`; `apply_unit_scale=True`.88 - USD: set `defaultPrim`, `upAxis=Y`, `metersPerUnit`. Confirm all meshes have89 `subdivisionScheme=none` for hard-surface.90 - USDZ: geometry must be triangulated; textures PNG or JPEG only; no absolute file paths.9192**4. Export** — choose the right bpy operator or CLI tool:93 - Blender GLB: `bpy.ops.export_scene.gltf(filepath=…, export_format='GLB', export_yup=True, export_apply=False)`94 Full parameter tables: **`references/blender-export.md`**.95 - Blender FBX: `bpy.ops.export_scene.fbx(filepath=…, axis_forward='-Z', axis_up='Y', apply_unit_scale=True)`96 - USD: `bpy.ops.wm.usd_export(filepath=…, generate_preview_surface=True, rename_uvmaps=True)`97 - Assimp batch: `assimp export "in.fbx" "out.glb" -fglb2 -tri -jiv -gsn -cts -fuv -icl`98 - USDZ: `usdzip --arkitAsset scene.usda out.usdz` then `usdchecker --arkit --strict out.usdz`99 - All scripts go in `.forge-build/out/` working dir; web handoff to `public/forge/<slug>-hero.glb`.100101**5. Validate** — non-negotiable gate:102 - GLB: `gltf_validator.exe --stdout out.glb` — zero errors required before proceeding.103 - USDZ: `usdchecker --arkit --strict out.usdz`.104 - **Degrade, never skip:** if `gltf_validator`/`usdchecker` is absent, fall back to105 `gltf-transform validate out.glb` (Khronos spec; install per `references/gltf-validator.md`106 §7) and note the substitution in FORGE.md. Validation is never silently skipped.107 - Invoke `Skill("forge-validate")` for the full manifold/normals/poly-budget/render gate.108109**6. Verify by render** — load the export into a FRESH scene and render one deterministic Cycles110 frame (EEVEE-Next is unsupported headless on Windows). Full copy-paste script + invocation:111 **`references/render-verify.md`** (mirrors forge-sim/export-cache.md §8).112 ```113 blender -b --python-exit-code 1 -P render_verify.py -- --in out.glb --out verify.png114 ```115 The script forces `render.engine='CYCLES'`, `cycles.device='CPU'`, a single fixed frame,116 `samples=32`, `seed=0` so re-verifies reproduce. **Guard before Read:** confirm `verify.png`117 exists and is **> 1 KB** (the script exits non-zero otherwise). Then `Read("verify.png")` to118 inspect visually. A missing / < 1 KB / all-black PNG = silent export failure (no geometry,119 dead material, or 100x scale) — fix the export and re-verify, not the verify script.120121**7. Hand off** (if web delivery):122 - Run `Skill("forge-optimize")` → Draco+Meshopt+KTX2 → `public/forge/<slug>-hero.glb`.123 forge-optimize owns the final `Skill("atelier-webgl")` call that wires the asset into the124 Three.js/R3F scene — export produces and validates the GLB, then hands it down the chain.125 - Naming contract: `public/forge/<slug>-hero.glb` + `<slug>-hero-poster.webp`.126 - Poster (reduced-motion fallback) must be rendered BEFORE the GLB handoff.127 - **Boundary:** Forge's asset gate is **`forge-validate`**; the WEB-runtime gate128 (CWV/LCP/CLS/INP, DOM a11y / alt text) is **`atelier-perf-a11y`**'s job, run downstream after129 `Skill("atelier-webgl")` — do not duplicate or skip it here.130131---132133## Engine import recipes134135### Unreal Engine 5.x136Full detail: **`references/engine-unreal.md`**137138- **1 UU = 1 cm.** Always set `convert_scene_unit=True` at import or export at cm scale from Blender139 (`apply_unit_scale=True`).140- **Z-up left-handed.** Enable `convert_scene=True` at import (handles Blender's Z-up/RH).141- **Interchange API** (UE 5.4+) is preferred for GLB; legacy `FbxImportUI` still works for FBX.142- **Nanite**: enable at import for meshes > 10k tris, Opaque/Masked only — NOT translucent,143 NOT morph targets, NOT mobile targets.144- **FBX naming**: collision hulls `UCX_MeshName_00`, LOD suffixes `_LOD0`…`_LOD3` in the same FBX.145- **Headless import**: `UnrealEditor-Cmd.exe project.uproject -run=pythonscript -script=import.py -unattended -nullrhi`146- **sRGB flags**: BaseColor/Emissive ON; Normal/ORM/Roughness/Metallic/AO OFF — the #1 cause of147 wrong materials on import.148149### Unity 6 (URP / HDRP)150Full detail: **`references/engine-unity.md`**151152- **1 unit = 1 meter.** Enable `useFileUnits=true` + `bakeAxisConversion=true` in `ModelImporter`.153 `bakeAxisConversion` bakes the axis fix into vertex data (not a root Transform rotation), which154 avoids breaking physics raycasts and NavMesh.155- **ORM channel layout differs per pipeline:**156 - URP Metallic Map: R=Metallic, G=AO, B=unused, **A=Smoothness** (1−Roughness).157 - HDRP Mask Map: R=Metallic, G=AO, B=DetailMask, **A=Smoothness**.158 - glTF spec: R=AO, G=Roughness, B=Metallic. Interchange auto-remaps; FBX does not.159- **glTFast** (`com.unity.cloud.gltfast` 6.17) is the recommended runtime/editor glTF importer.160- Headless import: `Unity.exe -projectPath … -batchmode -quit -executeMethod MyClass.Method`161- **AssetPostprocessor** `GetVersion()` must be incremented on every logic change or reimport won't fire.162163### Godot 4.3+164Full detail: **`references/engine-godot.md`**165166- **Prefer GLB** (single file, no sidecar). Commit `.import` files; gitignore `.godot/imported/`.167- **Headless import**: `godot.exe --headless --import --path C:\Project` (4.3+ flag; blocks until done).168- **Known bug (4.4.x)**: `ERROR: Parameter "t" is null` during headless glTF import — non-fatal,169 import still completes. Fixed in 4.6 / 4.5.x (PR #109116).170- **ORM**: glTF spec layout (R=AO, G=Roughness, B=Metallic). ORM textures must import as linear171 (`flags/srgb=false` in the `.import` file).172- **Collision suffixes** on mesh node names: `-col` (trimesh+visible), `-colonly` (trimesh+invisible),173 `-convcol` (convex+visible), `-convcolonly` (convex+invisible). Note: suffix applies to the *node*174 name, not the mesh data name (open bug #115869 as of 2026).175- **LOD**: set `meshes/generate_lods=true` in `.import` (uses meshoptimizer, zero runtime cost).176- **Normal maps**: Godot uses OpenGL convention (Y+ = up). Substance Painter default is OpenGL.177 DirectX normals (Y+ = down) must have the green channel inverted before import.178179---180181## Operating principles182183- **Format before execution.** Pick the right format for the target — then choose the right tool.184 GLB is the runtime hub; USDC is the pipeline hub; FBX is the DCC-to-engine legacy path.185 Never default to FBX when the target accepts GLB.186- **Gate at validation, not at guesswork.** Run `gltf_validator` (zero errors) and187 `Skill("forge-validate")` before declaring an export done. A file that passes the validator188 but renders blank has a material or lighting problem — verify by render.189- **Coordinate system is a first-class concern.** glTF = Y-up right-handed meters. Unreal = Z-up190 left-handed cm. Unity = Y-up left-handed meters. Godot = Y-up right-handed meters. Every191 export command must address the axis and unit explicitly — never rely on defaults.192- **Scripts, not inline code.** The `blender -b -P script.py -- args` invocation pattern lives in193 a file; do not paste multi-line Python into `--python-expr` for anything beyond trivial one-liners194 (the model pre-evaluates `$()` in SKILL.md code blocks — real logic belongs in scripts).195- **Run = call the Skill tool.** Saying "now run forge-optimize" in prose runs nothing.196 This skill's direct handoffs are `Skill("forge-validate")` (the gate) and `Skill("forge-optimize")`197 (web delivery); forge-optimize makes the final `Skill("atelier-webgl")` call — do not call it here.