# Iwsdk Migrate 0 5

> Migrate an existing IWSDK 0.4.x application to IWSDK 0.5.0. Use when upgrading across the 0.5 boundary, replacing Meta Spatial Editor or GLXF, removing retired Vite plugins, moving UIKitML to runtime-loaded assets, adopting native scene JSON, or resolving 0.5 migration errors.

- Skill: `facebook/iwsdk-migrate-0-5` (Agent Skill)
- Install (CLI): `npx skillmds@latest add facebook/iwsdk-migrate-0-5`
- Raw SKILL.md: https://api.skillmd.com/api/skills/facebook/iwsdk-migrate-0-5/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: facebook (https://skillmd.com/u/facebook)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/facebook/iwsdk-migrate-0-5

---


# Migrate IWSDK 0.4.x to 0.5.0

Upgrade an existing IWSDK application without losing behavior or authored
content. This is a migration, not a rewrite: preserve working runtime logic,
move only static composition into the native scene format, and prove parity in
the live runtime.

User context is in `$ARGUMENTS`.

## Scope

This skill is specifically for `0.4.x -> 0.5.0`. First inspect the installed
versions in `package.json` and the lockfile.

- If the project is already on `0.5.x`, diagnose the reported issue without
  replaying the migration.
- If it is older than `0.4.x`, apply the intervening release migrations first.
- If it is newer than `0.5.x`, use the skill for that release boundary instead.

The public comparison baseline for this guide is IWSDK `0.4.2`.

## Non-negotiable safety rules

1. Inspect `git status` before editing. Preserve all existing work; never reset,
   clean, or overwrite unrelated changes.
2. Create a recoverable checkpoint before transforming GLXF, scene files, or UI
   sources. Use the project's established version-control workflow; do not
   commit unless the user authorized commits.
3. Inventory the app before deleting a package. A package is removable only
   after all of its imports, config hooks, scripts, and generated outputs have
   been replaced.
4. Do not claim that a GLXF or Meta Spatial scene was migrated merely because
   the app compiles. IWSDK 0.5 has no GLXF runtime fallback; reproduce and
   verify the authored hierarchy in native scene JSON.
5. Keep dynamic behavior in TypeScript/JavaScript. Native scenes own static
   composition, component values, transforms, lights, panels, and player-space
   attachments—not runtime-dependent entity counts or game logic.

## Release boundary at a glance

| 0.4.x surface                                            | 0.5.0 replacement                                                                           |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| GLXF levels and `@iwsdk/glxf`                            | Native scene selected by `iwsdk.config.json` (or explicit low-level `WorldOptions.level`)  |
| Meta Spatial Editor and `@iwsdk/vite-plugin-metaspatial` | IWSDK managed editor in `@iwsdk/vite-plugin-dev`                                            |
| `@iwsdk/vite-plugin-uikitml` generated JSON              | Runtime parsing of source `.uikitml` files from `public/ui/`                                |
| `@iwsdk/vite-plugin-gltf-optimizer`                      | Pre-optimized source assets or the normal IWSDK glTF asset pipeline                         |
| `vite-plugin-mkcert`                                     | Cached, untrusted HTTPS certificate generated by `iwsdkDev()`                               |
| `PanelUI.config: './ui/panel.json'`                      | UIKitML manifest asset using a `BASE_URL`-safe source `.uikitml` URL                         |
| `PanelUI.maxWidth` / `maxHeight`                         | Entity transform scale plus the document's intrinsic dimensions                             |
| `features.spatialUI.kits`                                | `features.spatialUI.kit` and optional `componentSets`                                       |
| `GLXFComponentRegistry`                                  | `defineComponents([...])` plus native scene component props                                 |
| `LevelGLXFImporter` / `LevelEntityCreator`               | `World.loadLevel()` for native scenes / `World.createTransformEntity()` for dynamic objects |
| `iwsdkDev({ assetManifest, componentManifest, emulator })` | `iwsdk.config.json` plus bare `iwsdkDev()`                                                 |
| `iwsdkDev({ ai, workspace })`                            | Launch-time `iwsdk dev up` session flags                                                    |
| Remote Chef starter recipes / `@iwsdk/starter-assets`    | Common source, scenes, and guidance embedded in `@iwsdk/create`                              |
| `xr.features.lightEstimation`                            | Authored light components and `IBLTexture`/`IBLGradient`                                    |
| `render.defaultLighting` and implicit gradients          | Explicit dome and IBL components on each native scene root                                  |
| Static entities created in the startup callback          | Asset/component manifests plus native scene nodes                                           |
| Ad hoc custom-component registration                     | `defineComponents([...])`, shared by runtime and editor                                     |

The `Interactable` compatibility alias still exists, but new and migrated code
should use `RayInteractable`.

## Phase 1: Inventory before editing

Determine the package manager from the lockfile, then collect:

- every `@iwsdk/*` dependency and its installed version;
- every import or config call involving `glxf`, `metaspatial`, `compileUIKit`,
  `vite-plugin-uikitml`, `vite-plugin-gltf-optimizer`, `mkcert`, or
  `IWSDK_DISABLE_MKCERT`;
- every `World.create`, `World.loadLevel`, `PanelUI`, `ScreenSpace`,
  `Visibility`, `render.defaultLighting`, and `features.spatialUI` use;
- all `.glxf`, `.uikitml`, generated UI JSON, Meta Spatial project files, and
  generated glTF folders;
- static entity creation in startup code and the systems that later locate or
  manipulate those entities.

Classify each static object as one of:

- `asset`: glTF, UIKitML, or a parentless procedural `Object3D` prototype;
- `scene node`: a stable id, transform, asset reference, and component values;
- `player-space child`: content attached to player, camera/head, target-ray, or
  grip space;
- `dynamic`: keep in code because runtime state determines its existence.

Record the inventory in the migration report. This is the parity checklist.

## Phase 2: Align packages

Update every IWSDK package already used by the application to `0.5.0`. Keep
IWSDK packages on one version; do not mix `0.4.x` and `0.5.x` packages.

Remove these retired packages when present:

```text
@iwsdk/glxf
@iwsdk/vite-plugin-gltf-optimizer
@iwsdk/vite-plugin-metaspatial
@iwsdk/vite-plugin-uikitml
vite-plugin-mkcert
```

Do not add `@iwsdk/scene-composition` merely because it is new. It is already a
core dependency; add it directly only if application code imports its document,
validation, or composition APIs.

After editing `package.json`, use the project's package manager to update the
lockfile and installation. Do not delete the lockfile as a shortcut.

## Phase 3: Simplify Vite configuration

Remove imports and plugin entries for mkcert, Meta Spatial, UIKitML compilation,
and the glTF optimizer. A migrated config should follow this shape:

```ts
import { iwsdkDev } from '@iwsdk/vite-plugin-dev';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [iwsdkDev()],
  server: { host: '0.0.0.0', open: false },
});
```

Important behavior:

- Create `iwsdk.config.json` as the committed project authority. It selects the
  scene plus extensionless `assets.module` and `components.module` paths and
  stores serializable world/emulator options.
- Manifest-first dev servers always expose the managed editor. Choose AI mode,
  headed/headless launch, open behavior, and screenshot size with `iwsdk dev`
  flags rather than committed Vite options.
- The managed browser is the browser surface. Keep Vite `server.open` false so
  it does not create a second unmanaged tab.
- HTTPS is on by default. IWSDK caches an untrusted certificate without
  installing a local CA. Managed Playwright accepts it automatically; a
  physical headset shows the expected certificate warning.
- Use `iwsdkDev({ https: false })` only when HTTP is intentional. A custom
  `server.https` certificate takes precedence.
- Keep asset and component modules free of system imports and side effects so
  the editor can load them in its own realm.

Remove obsolete generated-output ignore rules only after confirming nothing
else creates those directories. Common stale paths are generated UIKit JSON,
Meta Spatial GLXF output, and plugin-generated glTF folders.

## Phase 4: Create shared manifests

Move the asset catalog into a dedicated module:

```ts
// src/assets.ts
import { AssetType, defineAssets } from '@iwsdk/core';

const publicAssetUrl = (path: string) =>
  `${import.meta.env.BASE_URL}${path.replace(/^\/+/u, '')}`;

export default defineAssets({
  environment: {
    name: 'Environment',
    type: AssetType.GLTF,
    url: publicAssetUrl('models/environment.glb'),
  },
  'settings-panel': {
    name: 'Settings Panel',
    type: AssetType.UIKitML,
    url: publicAssetUrl('ui/settings.uikitml'),
  },
});
```

An asset manifest may also contain parentless procedural `Object3D` prototypes.
Do not place a prototype in the Three scene or parent it before registration.

Declare application components separately:

```ts
// src/components.ts
import { defineComponents } from '@iwsdk/core';
import { MyBehavior } from './components/my-behavior.js';

export default defineComponents([MyBehavior]);
```

Point the project authority at both extensionless modules:

```json
{
  "$schema": "./node_modules/@iwsdk/core/dist/schemas/iwsdk-project.v1.schema.json",
  "version": "iwsdk.project.v1",
  "scene": "./public/scenes/main.iwsdk.scene.json",
  "assets": { "module": "./src/assets" },
  "components": { "module": "./src/components" },
  "world": {
    "xr": { "mode": "vr", "offer": "always" },
    "features": { "spatialUI": true }
  },
  "dev": { "emulator": { "device": "metaQuest3" } }
}
```

The Vite virtual module supplies the same exact exports to `World.create()`:

```ts
import { World } from '@iwsdk/core';
import projectOptions from 'virtual:iwsdk-project';

const world = await World.create(container, projectOptions);
```

Do not also import the manifests into `index.ts` or configure them in
`iwsdkDev()`. `iwsdk.config.json` is the single runtime/editor authority.

Delete GLXF registry setup and field mappers. Native scene component objects
store ordinary props keyed by the component id, and the shared component
manifest supplies their schemas to both runtime and editor.

## Phase 5: Replace GLXF or code-authored static layout

Create `public/scenes/main.iwsdk.scene.json`. A minimal asset-backed panel and
model look like this:

```json
{
  "version": "iwsdk.scene.v1",
  "units": "meters",
  "components": {},
  "resources": {},
  "nodes": [
    {
      "id": "environment",
      "content": { "type": "asset", "asset": "environment" },
      "transform": { "position": [0, 0, 0] },
      "components": { "LocomotionEnvironment": {} }
    },
    {
      "id": "settings-panel",
      "content": { "type": "asset", "asset": "settings-panel" },
      "transform": {
        "position": [0, 1.5, -2],
        "scale": 0.4
      },
      "components": {
        "RayInteractable": {},
        "ScreenSpace": {
          "top": "20px",
          "left": "20px",
          "width": "25vw",
          "height": "40vh"
        }
      }
    }
  ]
}
```

Migration rules:

- Node `id` is the stable runtime/editor identity. Preserve meaningful unique
  identifiers and use them from code; do not locate authored objects by array
  position or display name.
- Scene asset references must resolve in `src/assets.ts`.
- Move static component values into the scene. Keep systems and event logic in
  code.
- Preserve hierarchy with `children`. Use a node `parent` of type
  `player-space` for content attached to `player`, `camera`, `head`, target-ray,
  or grip spaces.
- Author the player origin under the top-level `player.transform`. Tracked
  head/controller transforms are runtime-owned and overridden by XR tracking.
- Use `visible: false` for authored initial visibility. Visibility and
  Transform are intrinsic editor properties, not ordinary add-component UI.
- Put fog, tone mapping, exposure, and shadow renderer settings under the scene
  `environment` object. Use `DomeGradient`/`DomeTexture` for the visible
  background and `IBLGradient`/`IBLTexture` for image-based lighting. There is
  no separate AR background policy; immersive AR remains transparent.
- Remove `render.defaultLighting`. IWSDK no longer injects either environment
  component. Author both gradient components for the former default look, or
  omit either independently when the scene intentionally has no background or
  no image-based lighting.
- Use IWSDK light components on scene nodes for authored ambient,
  hemisphere, directional, point, spot, or rect-area lights.

There is no supported 0.5 runtime path for `.glxf`. For a Meta Spatial/GLXF
project, use the old scene and screenshots as the visual reference, recreate
its static hierarchy in native scene JSON, then compare multiple editor and
runtime views before deleting legacy sources. If parity cannot be established,
stop and report that migration as incomplete.

Code-created dynamic entities can stay in code. It is valid to migrate the
static shell first and leave gameplay spawning, effects, and variable-count
objects in systems.

## Phase 6: Migrate UIKitML

Move source UIKitML files into `public/ui/` and delete the generated intermediate
JSON once no code references it. Change panel URLs from `.json` to `.uikitml`.

For editor-authored panels, prefer an `AssetType.UIKitML` manifest entry and an
asset-backed scene node, as shown above. `PanelUI` remains a compatibility path
for code-created panels, but it is hidden from generic editor authoring.

Remove `maxWidth` and `maxHeight` from `PanelUI`; 0.5 no longer performs a
second fit. The UIKitML document owns intrinsic dimensions and the entity
transform owns world scale. Give `ScreenSpace` explicit CSS dimensions rather
than relying on `auto`.

Replace spatial-UI kit configuration:

```ts
features: {
  spatialUI: {
    kit: 'horizon',
    componentSets: [],
  },
}
```

The default kit is `horizon`. UIKitML can load TTF fonts declared with
`@font-face`, including remote HTTPS URLs. Keep font loading CORS-compatible and
verify text after the document reports stable layout; do not add arbitrary
frame-count sleeps.

Locate and manipulate an authored panel by stable scene and element ids:

```ts
import { UIKitMLAsset } from '@iwsdk/core';

const panel = world.requireSceneObject<UIKitMLAsset>('settings-panel');
const saveButton = panel.requireElementById('save-button');
saveButton.addEventListener('click', onSave);
```

Do not traverse the entire Three scene looking for an anonymous
`UIKitDocument`, and do not key logic off generated JSON paths.

## Phase 7: Resolve behavior-level compatibility

Audit these cases even when TypeScript compiles:

- `createTransformEntity()` now gives every transform entity intrinsic
  `Visibility`. If old code adds `Visibility` itself, change it to set the
  existing value or assign `entity.object3D.visible`.
- Prefer `RayInteractable` over the deprecated `Interactable` alias.
- Remove `xr.features.lightEstimation`; IWSDK 0.5 no longer requests the
  unsupported feature. Replace its visual role with authored lights and IBL.
- Immersive AR always hides authored dome/background visuals for passthrough
  while retaining IBL. Re-test any app that previously expected a virtual AR
  background.
- `AssetManager.getGLTF()` returns a fresh clone by default. Use
  `{ shared: true }` only when shared mutable state is intentional.
- `ScreenSpace` `width`/`height: 'auto'` warns and falls back to viewport
  sizing. Author explicit dimensions and test browser resize plus XR exit.
- If custom UIKitML components were passed through `kits`, migrate them to
  `componentSets`; select the built-in collection with `kit`.
- If systems stored references to startup-created objects, replace static
  object plumbing with `world.getSceneObject`, `requireSceneObject`,
  `getSceneEntity`, or `requireSceneEntity` and stable node ids.
- Preserve cleanup functions for signal/query subscriptions and DOM/UIKit
  listeners. `World.destroy()` is now available for hot reload, tests, and
  multi-world hosts.

## Phase 8: Verify in increasing scope

Run the project's normal formatter, typecheck, tests, and production build.
Then verify the actual app:

1. Start the 0.5 dev server and wait for `npx iwsdk dev status --json` to report
   `browserConnected: true` and `browserCommandReady: true`.
2. Validate and open every native scene. Fix missing manifest assets,
   components, entity references, and file paths.
3. In Editor view, compare hierarchy, transforms, visibility, authored lights,
   panels, player-space children, and representative camera views with the
   pre-migration inventory.
4. In Runtime view, test every interaction and inspect console logs. Browser
   screenshots are runtime-only by design.
5. Enter and exit XR through both the application UI and the emulator/session
   controls. Confirm screen-space panels return after XR exit.
6. Resize the desktop window and verify UIKit layout, clipping, fonts, rounded
   corners, and perceived scale.
7. If a physical headset is available, open the reported HTTPS network URL,
   accept its self-signed-certificate warning, and enter XR.
8. Run a production build from a clean install using the committed lockfile.

Do not regenerate IWSDK's library reference corpus as part of an application
migration.

## Completion report

Return a concise report containing:

- detected source and target versions;
- package additions/removals;
- old GLXF/Meta Spatial/UIKit generated artifacts retained or deleted;
- scene, asset, component, and UIKitML files migrated;
- behavior changes made for compatibility;
- exact automated and live tests run;
- any visual or physical-headset checks still requiring a human;
- any unresolved parity gap that prevents calling the migration complete.

## Optional 0.5 modernization (not required for parity)

After migration is green, consider these additions separately: signal helpers
re-exported from `@iwsdk/core`, authored light components, raw XR frame/session
and hit-test helpers, `World.destroy()`, `World.loadSceneDocument()`, renderable
asset instantiation, player-space authoring, and managed scene review tools.
Do not mix these refactors into the compatibility pass unless the user asks.

