Validate scenes & regenerate schema
vgai’s scene formats (.vscn.json, .prefab.json) are defined by Zod schemas in the
engine — the single source of truth. Two commands keep everything in sync: generate-schema
regenerates the JSON Schema (which powers VS Code autocomplete) from those Zod definitions, and
validate-scenes checks that scene files only use components/fields the registry defines. This is
a terminal tutorial — the transcript below is real captured output (including a real,
honestly-reported gotcha).
> vgai-game@0.1.0 validate-scenes
> npx tsx packages/editor/template/validate-scenes.ts packages/editor/template --components
Scene validation failed with 53 error(s):
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "ArenaCharacterController" has unknown field "moveSpeed" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "ArenaCharacterController" has unknown field "jumpVelocity" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "ArenaCharacterController" has unknown field "gravity" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "ArenaCharacterController" has unknown field "snapToGround" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "OrbitCamera" has unknown field "distance" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "OrbitCamera" has unknown field "lookAtHeight" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "OrbitCamera" has unknown field "sensitivity" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "OrbitCamera" has unknown field "minPitch" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/arena.vscn.json: entity "LocalPlayer" component "OrbitCamera" has unknown field "maxPitch" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/fps.vscn.json: entity "Player" component "FPSController" has unknown field "moveSpeed" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/fps.vscn.json: entity "Player" component "FPSController" has unknown field "jumpVelocity" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/fps.vscn.json: entity "Player" component "FPSController" has unknown field "gravity" (valid: (none))
… (35 more lines) …
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "VehicleController" has unknown field "maxSteer" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "VehicleController" has unknown field "maxBrake" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "ChaseCamera" has unknown field "offsetX" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "ChaseCamera" has unknown field "offsetY" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "ChaseCamera" has unknown field "offsetZ" (valid: (none))
/Users/yueranyuan/volter/vgai-engine/.claude/worktrees/tutorial-site/packages/editor/template/public/examples/scenes/vehicle.vscn.json: entity "Vehicle" component "ChaseCamera" has unknown field "smooth" (valid: (none))
> vgai-game@0.1.0 generate-schema
> npx tsx scripts/generate-schema.ts
Generating JSON Schemas...
vscn.schema.json (169879 bytes)
prefab.schema.json (113983 bytes)
Done. Schemas written to packages/engine/schemas/ site/media/capture-cli.mjs -
Regenerate the JSON Schema from the Zod source.
Terminal window npm run generate-schemaThis writes
vscn.schema.jsonandprefab.schema.json(intopackages/engine/schemas/) from the Zod schemas — the transcript shows it completing cleanly. Run it after any schema change so editor autocomplete and the on-disk JSON Schema match the code; a pre-commit hook verifies it’s up to date. -
Validate scenes against the component registry.
Terminal window npm run validate-scenesThis is the scene guardrail: it walks the
.vscn.json/.prefab.jsonfiles and checks each entity’s components and fields against the engine’s component registry + Zod schemas, exiting non-zero on a violation (so bad scenes can’t land in CI).
Recap
New functionality
- Regenerated the JSON Schema from the Zod source (clean)
- Ran validate-scenes and learned to read its output critically
New concepts & skills
- Zod schemas are the single source of truth for .vscn/.prefab
- generate-schema keeps the on-disk JSON Schema (and autocomplete) in sync
- validate-scenes is the scene guardrail — but from the monorepo source layout it currently over-reports due to a cross-package GameComponent identity mismatch (false positives, not drift)
Next lesson → Scene schemas (manual)