# ProcTHOR source staging

This integration uses the official **ProcTHOR via MolmoSpaces** MuJoCo conversion. Its input house JSON matches ProcTHOR test house 0; its converted geometry and physics must not be described as identical to AI2-THOR. Browser validation uses the actual converted MuJoCo 3.5.0 scene.

`procthor-source-lock.json` records the source versions, archive URLs, byte sizes, SHA-256 hashes, selected archive members and attribution. It includes 18,000 selected files for 100 distinct houses: their MJCF file dependencies plus source scene metadata/maps and additional scene archive members. These counts are assets, not worlds.

Place the 111 official archives (100 scenes and 11 shared object archives) under an isolated cache, using each lock record's `path` (including `thor_SkyBox/Sky.tar.zst`). Then run from the repository root:

```sh
PYTHONPATH=scripts python -m worlds.stage_procthor_assets \
  --archives /path/to/isolated/archive-cache \
  --output .cache/procthor-source
```

The script requires `zstd`. It checks all archive hashes before decoding, rejects unsafe/nonregular members, verifies every selected member's exact size and digest, and checks all output conflicts before copying verified files. Repeating it with identical output is supported; existing edits and symlinks are rejected. Unselected source assets are not extracted. The source XML remains unchanged, retaining collision geometry and joints as well as browser visuals.

Scene version: `20251121_with_occupancy`. THOR objects: `20251117`. Upstream code pin: `735138876887eb64650742631b8dda841dd264bf`.

Scene and THOR models by the Allen Institute for AI (Ai2), licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). The lock links to the pinned upstream licensing helper, which distinguishes scene composition and referenced asset licenses.

Staging is not publication or complete browser acceptance. Catalogue/registry integration, full viewer checks, review, merge and production verification are separate gates.

The browser uses the house archive's unmodified `test_N_metadata.json` to label articulated parts and body-associated surfaces. Object categories and original part names come from its `objects[].name_map`; numbered category labels and surface numbers are presentation labels within this house. Exact object IDs, native joint/body IDs, and original part names remain available under **Source identifiers**. These labels do not rename simulator bindings, add robots, or define navigation tasks.

Hardware UI and lifecycle verification:

```sh
PROCTHOR_REPORT_DIR=/tmp/procthor-ui-review node scripts/verify_procthor_lifecycle.cjs
```

Run on a host with Playwright Chromium and a working hardware Vulkan renderer. The verifier opens the actual homepage, reloads the house through the visible picker 12 times, and checks the mobile joint controls, expanded source IDs, and focus restoration when Details closes. It records source-file digests and uses weak references for retired contexts so the measurement does not keep them alive. Texture/buffer figures count live WebGL objects, not VRAM bytes. Loading times include picker interaction and forced garbage collection, and the loopback source server is explicitly distinguished from production networking.

The startup viewer projection is `procthor-index.json` (budget 150 KB); it preserves scene capabilities, version/source information and inventory, while the full registry retains asset hashes and scene-specific dependency lists. Regenerate with `python -m worlds.viewer_index` and picker cards with `python -m worlds.procthor_catalogue`, using `PYTHONPATH=scripts`. Both support `--check` and are checked during staging.

Batch reset-pose verification: `PROCTHOR_REPORT_DIR=/tmp/procthor-batch node scripts/verify_procthor_batch.mjs` on the GPU host. Each scene loads through the normal adapter and renderer; body basis endpoints must agree within 10 micrometres. `PROCTHOR_SCENE=procthor-test-N` selects one scene for diagnosis; a selected-scene report is not whole-batch evidence. Camera composition and full-page interaction require separate checks. New opening cameras sample an interior viewpoint in the largest living room (or largest other room), maintain floor-edge clearance, prefer distance from object centres, and face an interior target along an in-room sightline. Bathroom targets use the mean position of source toilets, sinks, bathtubs and showers inside the selected room so the opening view includes fixture context. The presentation eye height and tilt are not recorded agent observations. House 0 retains its reviewed living-room camera.

`procthor-cameras.json` stores the selected presentation camera for houses 1–99, bound to both XML and house-JSON SHA-256 digests. Reproduce in isolated MuJoCo 3.5.0 with `PYTHONPATH=scripts python -m worlds.select_procthor_cameras --source worlds/procthor --lock worlds/procthor-source-lock.json --output /tmp/procthor-cameras.json`. All source members are verified before loading models. The selector tests 15 visible-group-0 rays per candidate, preferring rays without an obstruction within 1.25 m, then greater bounded depth. This is an occlusion heuristic, not a navigation or camera-quality benchmark; screenshots still require visual review. The original house-0 camera is retained.

Varied-house resource check: `PROCTHOR_SCENES=0,78,10,52,0,78,10,52,0,78,10,52,0 PROCTHOR_REPORT_DIR=/tmp/procthor-varied node scripts/verify_procthor_lifecycle.cjs`. The default remains 13 house-0 loads. Every selected house needs at least three loads; finish on house 0 for the mobile assertions. The check compares repeated loads of the same house, requires retired contexts to be collected or lost, and bounds post-GC heap. Object counts are not VRAM byte measurements; loopback timing is not production-network timing.

## Browser delivery

The browser keeps the original scene XML, body frames, joint ranges and source
links. It selects visual dependencies using the same inherited defaults and
MolmoSpaces group mask as final compilation. Unused skyboxes and collision-only
mesh references are not standalone browser downloads.

`node scripts/viewer/build-world-payloads.cjs` builds a separate, reproducible
transport layer from the source lock:

- OBJ source text is packed losslessly in shared gzip JSON bundles per prefab or
  scene asset directory. A bundle can include unrendered source parts; only the
  requested visual mesh buffers enter compilation. Prefabs are shared across
  houses. This is transport compression, not geometry simplification.
- Browser textures are derived WebP previews, quality85, at most1024px on either
  axis with aspect ratio and alpha retained. Original textures remain available
  through source files. The index records their source SHA-256 hashes.
- The `world-payloads` fingerprint group has its own immutable cache version,
  independent of UI styles and scripts. The index uses relative payload filenames.
- The index has a45second fetch/body deadline. Bundled asset loading uses six
  deduplicated HTTP transports and a45second selected-assets budget, including
  queue time. Cancellation and required-asset errors stop the whole batch.
- Decoded OBJ buffers share disposable object URLs; source mesh URLs are not
  requested again by the renderer. Progress counts selected source assets, not
  HTTP requests or compressed wire bytes.

Generated payloads are build outputs, not a second source catalogue. No additional
worlds, benchmark results, or source-native texture fidelity are claimed by them.
