Custom adapters & the conformance kit
A game that isn’t built on vgai’s first-party scene format can still run —
and be edited — inside the editor by shipping its own adapter: a module
in the game folder implementing the small RootAdapter contract (the same
seam described in the adapter seam). The editor
mounts, renders, and authors the game through it; your game keeps its own
architecture.
The one-page version
Section titled “The one-page version”- Your
vgai.project.jsondeclares a root with"adapter": { "module": "src/adapter.ts", "surface": "three" }(game-folder-relative path; no.., no absolute paths — andsurfaceis required, the object is validated strictly). There is nothing to declare about capability: the editor measures what your adapter reaches. - The module default-exports
{ id, mount(host) }. Your manifest’sadapter.surface—"three","canvas", or"dom"— picks the hostmount()receives and the mounted shape it returns (a realTHREE.Scene+THREE.Cameraforthree, a real pixiContainerstageforcanvas, the samecontainerelement it was handed fordom), plus adispose()and (what makes you editable) an optionalauthoringsurface:hierarchy(required),transforms,inspector,persistence— a react adapter has notransformssurface by design (no 3D object to bind). - Capability reach is measured against reality after mount
(
packages/editor/src/adapter-reach.ts): capture, transforms, inspector and persistence are each checked off the live adapter yourmount()returned. A missing capability never blocks the mount — it becomes a standing warning that names the mechanism to close it and never stops firing; the conformance kit surfaces the same gaps in its report before a user’s editor does. There is no summary grade and no acceptable partial state. - Prove it — the quick way, from a monorepo checkout:
vgai conformance /abs/path/to/your/game-folder(add--jsonfor a machine-readable report). Or call the kit directly from a vitest test in this repo:
import { runAdapterConformanceKit } from './adapter-conformance-kit';
const report = await runAdapterConformanceKit('/abs/path/to/your/game-folder');// throws a named `adapter-conformance: …` error on the first violationThe full contract — provider-by-provider assertions, the inspector probe
table, the three.js identity/dedupe rules, worked examples — lives in
docs/BRINGING-AN-EXISTING-GAME.md
in the repo. That guide is the canonical reference; this page is the map.
What the kit checks
Section titled “What the kit checks”runAdapterConformanceKit(projectRoot) runs headless (Node, no GPU/DOM) and
walks the whole contract in order: manifest loads → module path-guarded and
imported → default export shape-checked → mount() against a headless host
matching your root’s surface (ThreeHostContext for three, CanvasHostContext
for canvas, { container, game } for dom) → the mount-contract check for
that surface (three: scene/camera real instances; canvas: stage a real
pixi Container; dom: container is the same element handed to
mount(), plus drivesOwnLoop: false) → hierarchy roots non-empty, ids
unique, id↔object3D bridge bidirectional (three/canvas — explicitly
skipped, with a logged note, for dom) → transforms get/apply
round-trip, rotation is a quaternion (three/canvas only; dom has no
transform surface) → every non-readonly inspector field survives a
set→get round-trip (every kind) → persistence is honest (destination
named, save() resolves; every kind) → capability-reach warnings surfaced →
dispose() doesn’t throw.
Trust boundary, honestly stated
Section titled “Trust boundary, honestly stated”There is no sandbox: a custom adapter module runs with full editor-realm privileges, exactly like game entry code (single-realm, by design). The boundary is declaration honesty: the module path can’t escape the game folder, capabilities must be declared, and declared-vs-achieved is verified and logged. Don’t run game folders you don’t trust.