# Project export: Reframe

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Voice-driven extended reality. GPT Realtime hears you, GPT-5.6 Sol plans agentic, and Reframe segments the object and rebuilds the background from monocular depth and TSDF fusion without LiDAR.
- Devpost: https://devpost.com/software/reroom
- GitHub: https://github.com/Julian-AT/openai-build-week
- Video: https://www.youtube.com/embed/0-C9RTRu8j4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Julian S. (603 commits)

## Devpost submission (written by the team)

### Inspiration

Every AR furniture app can put a virtual couch in your room. Not one of them removes the couch that's already there, and that's the whole problem. You can't evaluate a new armchair when the old one is still sitting in the shot. Compositing a posed mesh over the feed is nothing, we've all done it. The hard part is the inverse: reconstructing the floor, wall, and skirting behind an object that is physically present and occluding those exact pixels in every frame you receive. We wanted to hold up a phone, point it at a chair, say "replace this with something warmer and red," and have it resolve on a live feed we never touch.

### What it does

Reframe is a live diminished/augmented reality room editor for iPhone. Place a catalog asset on the floor, swap a real object for one whose projected silhouette covers it, pull a real object out and reveal reconstructed background, undo instantly, offline included. Retrieval sits on a Qdrant vector DB with 20k+ furniture items, content-addressed. When you say "warmer and red" we embed it and run the ANN query under a hard pre-filter on the target region's measured footprint and clearance, so nothing that can't physically fit ever comes back, and we re-rank whatever survives on fit residual. Voice (GPT Realtime 2.1 over WebRTC), reticle, and tap all lower to the same intent struct and hit one resolver, because maintaining three of anything is how you get three sets of bugs. Sessions serialize to a durable .rfcap file so you can replay and re-edit from a Next.js web twin. Readiness is per-capability and we don't lie about it: replace lights up the moment a coarse proxy exists, remove holds until reveal coverage is good enough, and there's a visible "Healing" state while inpainting is still converging.

### How we built it

The rule the whole thing hangs on: models are advisory, deterministic code owns identity, geometry, revision, commit, restore. GPT-5.6 Sol plans through five read-only tools and hands back a proposed edit. It cannot commit anything. A deterministic validator does that, and only that. It runs collision and clearance in the metric scene frame, checks the support relation against the detected supporting plane, and computes a rasterized cover score by projecting the candidate mesh under the current pose against the target mask, \( c = |S_{\text{new}} \cap S_{\text{target}}| / |S_{\text{target}}| \). The edit lands only if \( c \geq \tau \). Commit is one compare-and-swap on a monotonic revision counter behind an idempotency key, so a stale or replayed proposal is a no-op instead of a corruption. Nothing a model returns ever writes scene state. Geometry runs on two tracks, and the split wasn't a design choice so much as something the latency forced on us. First-edit time cannot wait on dense reconstruction. So the fast track fuses ARKit VIO poses and plane anchors with a SAM 3.1 video track, carving the mask sequence into a voxel occupancy volume that we deliberately dilate rather than erode. That gives you an editable proxy in a couple of seconds that is guaranteed to over-cover the real object, which is exactly the failure direction you want. The dense track runs DA3 monocular metric depth and solves a per-frame affine alignment \( \hat{d} = a\,d_{\text{pred}} + b \) against sparse VIO depth with robust least squares, throwing out any frame whose residual is too high, then fuses the accepted frames into an Open3D CUDA TSDF with truncation a few voxels wide. Occlusion masks and metric extents tighten asynchronously as the SDF firms up. Reveal texturing follows a strict precedence and we never break it: reprojected observed pixels from other views first, deterministic planar/homography fills second, LaMa only for texels no geometry can account for. A synthesized texel is never permitted to paint over an observed one. On device it's SwiftUI, ARKit, RealityKit, with a Metal compositing fallback for the diminished-reality passes RealityKit won't express without a fight. The gateway is Bun and TypeScript over SQLite in WAL mode, fronting an in-process GPU lane scheduler. Vision workers are Python 3.12 in isolated Docker containers behind that scheduler. One JSON Schema contract defines the binary FramePacket and every message on the wire, and we implemented it three times, Swift, TypeScript, Python, so the bytes mean the same thing everywhere. Sol was good at taking loose constraints and turning them into schema-ready field definitions and leaning on the contract boundaries until they held. Codex wrote a large chunk of the cross-runtime implementation and its tests. Four people don't get this far in a week without both.

### Challenges we ran into

Base iPhone 17, no LiDAR. Every metric millimetre is VIO plus learned depth, and learned depth is affine-ambiguous in scale, full stop. So the alignment stage will drop a frame before it fuses a plausible-but-wrong one, because a single bad scale estimate doesn't degrade the TSDF locally, it contaminates it globally. The thing that ate the most hours, embarrassingly, was coordinate conventions. ARKit is right-handed looking down \( -Z \), OpenCV looks down \( +Z \), and the flip \( \text{diag}(1,-1,-1) \) is its own inverse. Apply it an even number of times and everything looks almost right, which is the worst kind of wrong because it survives eyeballing. We now carry the handedness tag next to every matrix and gate it in CI with a known-ray reprojection test that fails on any parity mismatch. Keeping the first edit off the serial depth-to-TSDF-to-mesh chain is what forced the two-track design in the first place. And one GPU multiplexing segmentation, depth, fusion, reveal, and naming will absolutely starve itself if you let it, so there's an explicit priority-lane coordinator with preemption in front of it that keeps a reveal job from ever blocking the interactive segmentation lane. Reveal isn't instant and we refused to pretend otherwise, so we pinned it to real gates: \( p_{10} \) and median reveal coverage across eight sampled viewpoints.

### Accomplishments we're proud of

The deterministic core is tested code, not a slide. The binary FramePacket contract, the coordinate conventions, the CAS transaction and undo model with idempotency keys, the cover-score math with its exact thresholds, crash-safe .rfcap capture on device, HMAC room-scoped session auth, the GPU lane coordinator, the 20k+ item catalog pipeline. Unit-tested across all three runtimes. And the invariants actually held: the camera feed is never punched per pixel, a model never writes scene state, synthesized texels never overwrite observed ones, and the phone never claims a capability is ready before the artifact behind it is verified and live.

### What we learned

Put every model behind a typed provider boundary. You will change your mind about which model you're using. You will not get to change your mind about what identity means, so don't couple them. Readiness is per-capability, "editable" was never one bit, and people trust the system more when replace unlocks before remove and the UI just tells them why. A dilated mask is invisible, a leaked chair leg is not, so conservative geometry wins over clever geometry every single time. Contracts-first is the only reason four people and two AI collaborators could work across three languages without the implementations quietly drifting apart. And in a fusion pipeline a dropped frame costs you nothing, you get the next one, but a confidently wrong frame poisons everything downstream of it.

### What's next

Closing the gates we set. A full replace on a live SAM 3.1 track, end to end. LaMa and the CUDA TSDF running in production at a budgeted cadence. Live voice turns and Qdrant retrieval over the full 20k+ catalog under real session load. FPS, thermal, and visual-quality validation on actual hardware, not a bench. After that, catalog ingestion at IKEA scale, the Mode B1 photoreal Gaussian-splat twin in the web viewer, tabletop and shelf support relations, multi-object scenes, and eventually multi-room capture. None of it worth giving up an invariant to get.

## README (from the GitHub repository)

# Reframe

> A live spatial design system for understanding and reshaping real rooms.

![OpenAI Build Week](https://img.shields.io/badge/OpenAI-Build_Week-000000?style=flat-square&logo=openai&logoColor=white)
![iOS 18+](https://img.shields.io/badge/iOS-18%2B-111111?style=flat-square&logo=apple&logoColor=white)
[![MIT License](https://img.shields.io/badge/license-MIT-d7ff64?style=flat-square)](LICENSE)

![Apartment captured as a spatial point cloud in Reframe](assets/readme/reframe-room-point-cloud.png)

Reframe turns a live room capture into reversible spatial edits. The native
iPhone experience combines ARKit tracking, spatial understanding, prepared 3D
assets, and an OpenAI design assistant without giving cloud services control of
the render loop or canonical scene state.

Instead of treating AR as a model viewer, Reframe understands what already
occupies the room, finds an asset that physically fits, and previews the change
against the live camera. The four operations are **place**, **replace**,
**remove**, and **restore**.

Read the [Reframe technical paper](REFRAME_PAPER.pdf) for the complete system
architecture, contracts, and execution model.

## How it works

```mermaid
%%{init: {"theme": "neutral"}}%%
flowchart TB
    subgraph DEVICE["ON DEVICE | 60 FPS AND OFFLINE SAFE"]
        direction LR
        ARKIT["ARKit session<br/>poses, planes, frame quality"]
        ADMISSION["Frame admission<br/>quality, baseline, backpressure"]
        REPLICA["Local scene replica<br/>revisioned artifact cache"]
        RENDER["RealityKit compositor<br/>camera, occlusion, reveal, assets"]
        ARKIT --> ADMISSION
        ARKIT --> RENDER
        REPLICA --> RENDER
    end

    subgraph PERCEPTION["SPATIAL UNDERSTANDING | ASYNCHRONOUS GPU PATHS"]
        direction LR
        INGEST["Typed frame ingest"]
        FAST["Fast path<br/>semantic track, silhouette volume"]
        DENSE["Dense path<br/>metric depth, alignment, TSDF"]
        OBJECT["Object model<br/>identity, bounds, support"]
        GEOMETRY["Scene geometry<br/>surfaces, dimensions, occluders"]
        REVEAL["Reveal synthesis<br/>observed atlas, bounded fill"]
        ARTIFACTS["Versioned spatial artifacts"]
        INGEST --> FAST --> OBJECT
        INGEST --> DENSE --> GEOMETRY
        OBJECT --> REVEAL
        GEOMETRY --> REVEAL
        OBJECT --> ARTIFACTS
        GEOMETRY --> ARTIFACTS
        REVEAL --> ARTIFACTS
    end

    subgraph CONTROL["DESIGN CONTROL | BOUNDED AI AND DETERMINISTIC TOOLS"]
        direction LR
        INTENT["Voice, tap, pointer context"]
        CATALOG["Eligible asset catalog<br/>dimensions, provenance, render profile"]
        PLAN["Typed design proposal"]
        VALIDATE{"Deterministic validation<br/>target, fit, clearance, revision"}
        PREVIEW["Preview transaction"]
        INTENT --> PLAN
        CATALOG --> PLAN
        PLAN --> VALIDATE --> PREVIEW
    end

    subgraph AUTHORITY["SCENE AUTHORITY | PREVIEW BEFORE COMMIT"]
        direction LR
        ACTIVATE["Local preview activation"]
        CONFIRM{"User confirmation"}
        COMMIT["Compare and swap commit<br/>undo, restore, replay"]
        ACTIVATE --> CONFIRM --> COMMIT
    end

    ADMISSION --> INGEST
    ARTIFACTS --> REPLICA
    ARTIFACTS --> PLAN
    PREVIEW --> ACTIVATE
    PREVIEW --> REPLICA
    COMMIT --> REPLICA
```

Reframe separates rendering, inference, planning, and state mutation. ARKit is
the metric pose authority. Accepted frames fan out into a fast semantic path
for target identity and a dense reconstruction path for surfaces, dimensions,
and occlusion. Both paths publish versioned artifacts without entering the 60
FPS render loop.

Voice and tap input carry explicit pointer and scene context. The agent may
interpret intent and retrieve eligible assets, but deterministic tools resolve
the target, validate physical fit, and reject stale revisions. The iPhone
renders the proposal from its local replica. Only user confirmation creates a
new scene revision, and every committed edit remains undoable and restorable.

## Workspace

| Area | Responsibility |
| --- | --- |
| [iOS](apps/ios/README.md) | Native capture, interaction, and AR rendering |
| [API](apps/api/README.md) | Trusted gateway, sessions, transactions, and service coordination |
| [Vision](apps/vision/README.md) | Private segmentation, depth, geometry, and reveal inference |
| [Web](apps/web/README.md) | Room model, capture handoff, and replay experience |
| [Agent](packages/agent/README.md) | Bounded Responses and Realtime adapters |
| [Catalog](packages/catalog/README.md) | Acquisition, preparation, retrieval, and delivery of 3D assets |
| [Protocol](packages/protocol/README.md) | Canonical schemas, coordinates, and transaction behavior |

## Development setup

Reframe is a multi-runtime spatial system, not a one-command application. The
complete experience spans a physical iPhone, a trusted gateway, Qdrant, private
GPU vision workers, the web client, prepared 3D assets, and optional OpenAI
voice and planning.

### Prerequisites

| Requirement | Used for |
| --- | --- |
| macOS, Xcode with the iOS 18 SDK, and a physical iPhone | ARKit capture and the live spatial editor |
| [Bun 1.3.11](https://bun.sh/) | Gateway, web client, and TypeScript packages |
| Python 3.12 and `uv >=0.9.26,<0.12` | Vision service and its verification toolchain |
| Docker with Compose | Local gateway and Qdrant topology |
| CUDA-capable GPU plus prepared DA3 and SAM sources/checkpoints | Live depth, geometry, and target tracking |
| OpenAI API credentials | Realtime voice and bounded design-agent turns |

Install the JavaScript workspace with `bun install --frozen-lockfile`, then
bring up the system capability by capability:

| Order | Component | Required configuration | Runbook |
| ---: | --- | --- | --- |
| 1 | Gateway + Qdrant | Gateway, room-signing, and Qdrant secrets; absolute data directory | [API setup](apps/api/README.md) |
| 2 | Vision workers | Private service token, profile, model sources, checkpoints, revisions, and hashes | [Vision setup](apps/vision/README.md) |
| 3 | Asset catalog | Authorized source frontier, external asset store, processor tools, embeddings, and Qdrant access | [Catalog setup](packages/catalog/README.md) |
| 4 | Web client | Gateway URL and server-side token for connected routes | [Web setup](apps/web/README.md) |
| 5 | iPhone app | Gateway URL, room ID, short-lived room credential, and signing configuration | [iOS setup](apps/ios/README.md) |

The services must agree on gateway and vision URLs, scoped tokens, room/session
identity, and artifact storage. Model repositories and checkpoints are prepared
explicitly; workers do not download them at startup. The landing-page point
cloud viewer can run without those services, but it is a **UI-only preview**,
not a full Reframe deployment.

Run the repository checks with:

```sh
bun run check
bun run test:swift
```

## Stack

| Layer | Technology |
| --- | --- |
| Native | Swift 6.1, SwiftUI, ARKit, RealityKit |
| Web | Next.js 16, React 19, Three.js |
| Gateway | Bun, TypeScript, Hono, SQLite |
| Vision | Python 3.12, FastAPI, provider-isolated GPU models |
| AI | OpenAI Responses API and Realtime API |
| Assets | GLB, USDZ, Qdrant semantic retrieval |

## Design principles

- The live renderer never waits for the network or an inference worker.
- ARKit remains the pose authority for healthy native sessions.
- AI tools are bounded, typed, and unable to commit canonical state.
- Prepared assets are activated only after dimensions, provenance, and hashes verify.
- Committed edits remain available locally when cloud services disconnect.

## License

Reframe is available under the [MIT License](LICENSE). Built for OpenAI Build
Week 2026.

<p align="center">
  <a href="https://www.linkedin.com/in/julian-at/"><img src="https://media.licdn.com/dms/image/v2/D4D03AQEP4yaSLcLxfQ/profile-displayphoto-crop_800_800/B4DZ0F5UKXIIAI-/0/1773920405497?e=1786579200&amp;v=beta

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 208 recognized source files, 1129 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- React (technology) — detected in the code
- Swift (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 254)

```
.editorconfig
.env.example
.gitattributes
.github/workflows/ci.yml
.gitignore
.husky/install.mjs
.husky/pre-commit
apps/api/.env.example
apps/api/.gitignore
apps/api/compose.dev.yml
apps/api/package.json
apps/api/README.md
apps/api/src/agent-read-tools.ts
apps/api/src/agent-smoke.ts
apps/api/src/agent-turn-service.ts
apps/api/src/agent-turn.ts
apps/api/src/asset-delivery-service.ts
apps/api/src/cached-agent-turn-service.ts
apps/api/src/durable-edit-transaction-service.ts
apps/api/src/durable-session-store.ts
apps/api/src/edit-transaction-service.ts
apps/api/src/gpu-lane-coordinator.ts
apps/api/src/inference-client.ts
apps/api/src/inference-protocol.ts
apps/api/src/known-target-registry.ts
apps/api/src/live-placement-agent-smoke.ts
apps/api/src/live-placement-agent.ts
apps/api/src/main.ts
apps/api/src/protocol.ts
apps/api/src/room-agent-service.ts
apps/api/src/runtime-readiness.ts
apps/api/src/scene-object-registry.ts
apps/api/src/server.ts
apps/api/src/strict-json.ts
apps/api/test/agent-read-tools.test.ts
apps/api/test/agent-turn-service.test.ts
apps/api/test/asset-delivery-service.test.ts
apps/api/test/cached-agent-turn-service.test.ts
apps/api/test/capture-routes.test.ts
apps/api/test/durable-edit-transaction-service.test.ts
apps/api/test/durable-session-store.test.ts
apps/api/test/edit-routes.test.ts
apps/api/test/edit-transaction-service.test.ts
apps/api/test/gpu-lane-coordinator.test.ts
apps/api/test/inference-client.test.ts
apps/api/test/inference-protocol.test.ts
apps/api/test/known-target-registry.test.ts
apps/api/test/live-placement-agent-smoke.test.ts
apps/api/test/live-placement-agent.test.ts
apps/api/test/runtime-readiness.test.ts
apps/api/test/scene-object-registry.test.ts
apps/api/test/server.test.ts
apps/api/test/turn-routes.test.ts
apps/api/tsconfig.build.json
apps/api/tsconfig.json
apps/ios/Packages/SpatialCore/Package.swift
apps/ios/Packages/SpatialCore/Sources/CaptureCore/FrameBuffer.swift
apps/ios/Packages/SpatialCore/Sources/CaptureCore/ReplacementTargetProjection.swift
apps/ios/Packages/SpatialCore/Sources/CaptureCore/RFCapture.swift
apps/ios/Packages/SpatialCore/Sources/CaptureCore/SpatialObservations.swift
apps/ios/Packages/SpatialCore/Sources/CaptureCore/TargetAcquisition.swift
apps/ios/Packages/SpatialCore/Sources/EditCore/SceneReplica.swift
apps/ios/Packages/SpatialCore/Sources/RenderCore/AssetDelivery.swift
apps/ios/Packages/SpatialCore/Sources/RenderCore/LocalPlacementPreview.swift
apps/ios/Packages/SpatialCore/Sources/RenderCore/RealityKitAssetLoader.swift
apps/ios/Packages/SpatialCore/Sources/SpatialProtocol/FramePacket.swift
apps/ios/Packages/SpatialCore/Sources/SpatialProtocol/RoomSession.swift
apps/ios/Packages/SpatialCore/Sources/SpatialProtocol/SpatialTypes.swift
apps/ios/Packages/SpatialCore/Sources/SpatialProtocol/TargetSeed.swift
apps/ios/Packages/SpatialCore/Tests/CaptureCoreTests/ReplacementTargetProjectionTests.swift
apps/ios/Packages/SpatialCore/Tests/CaptureCoreTests/RFCaptureTests.swift
apps/ios/Packages/SpatialCore/Tests/CaptureCoreTests/SpatialObservationTests.swift
apps/ios/Packages/SpatialCore/Tests/CaptureCoreTests/TargetSeedBuilderTests.swift
apps/ios/Packages/SpatialCore/Tests/EditCoreTests/SceneReplicaTests.swift
apps/ios/Packages/SpatialCore/Tests/RenderCoreTests/PlacementPreviewTests.swift
apps/ios/Packages/SpatialCore/Tests/SpatialProtocolTests/FramePacketTests.swift
apps/ios/Packages/SpatialCore/Tests/SpatialProtocolTests/RoomSessionTests.swift
apps/ios/Packages/SpatialCore/Tests/SpatialProtocolTests/TargetSeedTests.swift
apps/ios/README.md
apps/ios/Reframe/Reframe.xcodeproj/project.pbxproj
apps/ios/Reframe/Reframe.xcodeproj/xcshareddata/xcschemes/Reframe.xcscheme
apps/ios/Reframe/Reframe/App.swift
apps/ios/Reframe/Reframe/CameraSurface.swift
apps/ios/Reframe/Reframe/GatewayClient.swift
apps/ios/Reframe/Reframe/Info.plist
apps/ios/Reframe/Reframe/RealtimeVoice.swift
apps/ios/Reframe/Reframe/RootView.swift
apps/ios/Reframe/Reframe/SpatialSession.swift
apps/vision/.env.example
apps/vision/.python-version
apps/vision/package.json
apps/vision/pyproject.toml
apps/vision/README.md
apps/vision/src/reframe_vision/__init__.py
apps/vision/src/reframe_vision/app.py
apps/vision/src/reframe_vision/conservative_geometry.py
apps/vision/src/reframe_vision/contracts.py
apps/vision/src/reframe_vision/da3_metric.py
apps/vision/src/reframe_vision/main.py
apps/vision/src/reframe_vision/providers.py
apps/vision/src/reframe_vision/reveal.py
apps/vision/src/reframe_vision/runtime.py
apps/vision/src/reframe_vision/sam3_provider.py
apps/vision/src/reframe_vision/scheduler.py
apps/vision/src/reframe_vision/target_geometry.py
apps/vision/src/reframe_vision/target_track.py
apps/vision/src/reframe_vision/wire.py
apps/vision/tests/__init__.py
apps/vision/tests/test_conservative_geometry.py
apps/vision/tests/test_da3_metric.py
apps/vision/tests/test_provider.py
apps/vision/tests/test_reveal.py
apps/vision/tests/test_sam3_provider.py
apps/vision/tests/test_target_geometry.py
apps/vision/tests/test_target_track.py
apps/vision/tests/test_wire.py
apps/vision/uv.lock
apps/web/next-env.d.ts
apps/web/next.config.ts
apps/web/package.json
[134 more files omitted for size]
```

### Dependencies

- apps/api/package.json: @reframe/agent@workspace:*, @reframe/catalog@workspace:*, @reframe/protocol@workspace:*, @types/bun@1.3.14, @types/node@24.13.3, hono@4.12.31, typescript@6.0.2
- apps/vision/pyproject.toml: addict@==2.4.0, einops@==0.8.1, fastapi@==0.139.2, httpx@==0.28.1, huggingface-hub@==0.30.2, imageio@==2.37.0, numpy@==1.26.4, omegaconf@==2.3.0, opencv-python-headless@==4.11.0.86, pillow@==12.1.1, pydantic@==2.13.4, safetensors@==0.5.3, torch@==2.6.0, torchvision@==0.21.0, tqdm@==4.67.1, uvicorn@==0.51.0
- apps/web/package.json: @reframe/agent@workspace:*, @reframe/protocol@workspace:*, @types/bun@1.3.14, @types/node@22.19.7, @types/react@19.2.17, @types/react-dom@19.2.3, @types/three@0.185.1, next@16.2.10, react@19.2.7, react-dom@19.2.7, server-only@0.0.1, three@0.185.1, typescript@6.0.2
- package.json: @biomejs/biome@2.5.4, husky@9.1.7, lint-staged@17.1.0, turbo@2.10.5
- packages/agent/package.json: @reframe/protocol@workspace:*, @types/bun@1.3.14, openai@6.48.0, typescript@6.0.2
- packages/catalog/package.json: @qdrant/js-client-rest@1.18.0, @types/bun@1.3.14, openai@6.48.0, typescript@6.0.2
- packages/protocol/package.json: @types/bun@1.3.11, typescript@5.9.3

### Recent commits (newest first)

- Merge branch 'main' of https://github.com/Julian-AT/openai-build-week
- fix(web): scope Vercel config to Next.js app
- docs: link team portraits
- docs: add Reframe team portraits
- fix(web): configure Vercel monorepo output
- docs: add Codex plan acknowledgment
- docs: detail the Reframe execution model
- docs: simplify how Reframe works
- docs: clarify setup and elevate architecture flow
- docs: refine project README and hero
- feat(web): reconstruct apartment point-cloud surface
- docs: add workspace and component READMEs with room-model hero image
- docs: add Reframe technical paper
- feat(web): polish the apartment scene
- feat(web): add a labeled 3D model view of the apartment scan
- feat(web): display the apartment point cloud
- chore: trim comments to essentials
- docs: keep only the Master Technical Prompt
- feat(web): compare the room point cloud against a 3D model
- fix(protocol): bind typed-turn spatial context from durable state

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "reframe",
  "version": "0.1.0",
  "private": true,
  "packageManager": "bun@1.3.11",
  "workspaces": [
    "apps/api",
    "apps/vision",
    "apps/web",
    "packages/*"
  ],
  "scripts": {
    "build": "turbo run build",
    "check": "bun run check:quick && bun run test && bun run build && bun run check:lockfiles",
    "check:lockfiles": "test -f bun.lock && test -f apps/vision/uv.lock && test ! -e package-lock.json && test ! -e pnpm-lock.yaml && test ! -e yarn.lock",
    "check:quick": "bun run format:check && bun run lint && bun run typecheck",
    "dev": "turbo run dev",
    "format": "biome check --write package.json turbo.json biome.json lint-staged.config.mjs apps/api/src apps/api/test apps/web/src packages/agent/src packages/catalog/src packages/protocol/src && turbo run format",
    "format:check": "biome format package.json turbo.json biome.json lint-staged.config.mjs apps/api/src apps/api/test apps/web/src packages/agent/src packages/catalog/src packages/protocol/src && turbo run format",
    "lint": "biome lint package.json turbo.json biome.json lint-staged.config.mjs apps/api/src apps/api/test apps/web/src packages/agent/src packages/catalog/src packages/protocol/src && turbo run lint",
    "precommit": "lint-staged",
    "prepare": "bun .husky/install.mjs",
    "test": "turbo run test",
    "test:swift": "swift test --package-path apps/ios/Packages/SpatialCore",
    "typecheck": "turbo run typecheck"
  },
  "devDependencies": {
    "@biomejs/biome": "2.5.4",
    "husky": "9.1.7",
    "lint-staged": "17.1.0",
    "turbo": "2.10.5"
  }
}

```

### apps/vision/package.json

```
{
  "name": "@reframe/vision",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "uv run --frozen python -m compileall -q src tests",
    "dev": "uv run --frozen python -m reframe_vision.main",
    "format": "uv run --frozen ruff format --check .",
    "lint": "uv run --frozen ruff check .",
    "test": "uv run --frozen pytest -q",
    "typecheck": "uv run --frozen basedpyright"
  }
}

```

### packages/catalog/package.json

```
{
  "name": "@reframe/catalog",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts"
  },
  "scripts": {
    "build": "tsc -p tsconfig.build.json",
    "sync": "bun src/cli.ts",
    "test": "bun test test",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@qdrant/js-client-rest": "1.18.0",
    "openai": "6.48.0"
  },
  "devDependencies": {
    "@types/bun": "1.3.14",
    "typescript": "6.0.2"
  }
}

```

### packages/agent/package.json

```
{
  "name": "@reframe/agent",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts"
  },
  "engines": {
    "bun": "1.3.11"
  },
  "scripts": {
    "build": "tsc -p tsconfig.build.json",
    "test": "bun test test",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@reframe/protocol": "workspace:*",
    "openai": "6.48.0"
  },
  "devDependencies": {
    "@types/bun": "1.3.14",
    "typescript": "6.0.2"
  }
}

```

### packages/protocol/package.json

```
{
  "name": "@reframe/protocol",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts",
    "./schemas/*": "./schemas/*",
    "./transaction": "./src/transaction.ts"
  },
  "scripts": {
    "build": "bun build ./src/index.ts --outdir ./dist --target bun",
    "format": "biome check --write src test package.json",
    "lint": "biome lint src test package.json",
    "test": "bun test test",
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "@types/bun": "1.3.11",
    "typescript": "5.9.3"
  }
}

```

### apps/api/package.json

```
{
  "name": "@reframe/api",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "engines": {
    "bun": "1.3.11"
  },
  "scripts": {
    "agent:smoke": "bun src/agent-smoke.ts",
    "build": "tsc -p tsconfig.build.json",
    "dev": "bun --watch src/main.ts",
    "dev:local": "docker compose -f compose.dev.yml up --build --remove-orphans",
    "start": "bun src/main.ts",
    "test": "bun test test",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@reframe/agent": "workspace:*",
    "@reframe/catalog": "workspace:*",
    "@reframe/protocol": "workspace:*",
    "hono": "4.12.31"
  },
  "devDependencies": {
    "@types/bun": "1.3.14",
    "@types/node": "24.13.3",
    "typescript": "6.0.2"
  }
}

```

### apps/web/package.json

```
{
  "name": "@reframe/web",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "engines": {
    "bun": "1.3.11"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "typecheck": "next typegen && tsc --noEmit",
    "test": "bun test --pass-with-no-tests"
  },
  "dependencies": {
    "@reframe/agent": "workspace:*",
    "@reframe/protocol": "workspace:*",
    "next": "16.2.10",
    "react": "19.2.7",
    "react-dom": "19.2.7",
    "server-only": "0.0.1",
    "three": "0.185.1"
  },
  "overrides": {
    "postcss": "8.5.20"
  },
  "devDependencies": {
    "@types/bun": "1.3.14",
    "@types/node": "22.19.7",
    "@types/react": "19.2.17",
    "@types/react-dom": "19.2.3",
    "@types/three": "0.185.1",
    "typescript": "6.0.2"
  }
}

```

### apps/vision/pyproject.toml

```
[project]
name = "reframe-vision"
version = "1.0.0"
description = "Private Reframe vision orchestration service"
requires-python = ">=3.12,<3.14"
dependencies = [
  "fastapi==0.139.2",
  "httpx==0.28.1",
  "numpy==1.26.4",
  "pillow==12.1.1",
  "pydantic==2.13.4",
  "uvicorn==0.51.0",
]

[project.optional-dependencies]
torch = ["torch==2.6.0"]
da3 = [
  "addict==2.4.0",
  "einops==0.8.1",
  "huggingface-hub==0.30.2",
  "imageio==2.37.0",
  "omegaconf==2.3.0",
  "opencv-python-headless==4.11.0.86",
  "safetensors==0.5.3",
  "tqdm==4.67.1",
  "torchvision==0.21.0",
]

[dependency-groups]
dev = [
  "basedpyright==1.39.9",
  "pytest==9.1.1",
  "ruff==0.15.22",
]

[build-system]
requires = ["hatchling==1.28.0"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/reframe_vision"]

[tool.uv]
required-version = ">=0.9.26,<0.12"

[tool.uv.sources]
torch = [
  { index = "pytorch-cu124", marker = "sys_platform == 'linux' and platform_machine == 'x86_64'" },
]
torchvision = [
  { index = "pytorch-cu124", marker = "sys_platform == 'linux' and platform_machine == 'x86_64'" },
]

[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true

[tool.pytest.ini_options]
addopts = "--strict-config --strict-markers"
testpaths = ["tests"]

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
  "B008",
  "C901",
  "COM812",
  "D100",
  "D101",
  "D102",
  "D103",
  "D104",
  "D105",
  "D107",
  "D203",
  "D213",
  "EM101",
  "TC002",
  "TC003",
  "TRY003",
]

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["EM101", "PLR0913", "PLR2004", "S101", "S105", "TC001", "TRY003"]

[tool.basedpyright]
include = ["src", "tests"]
pythonVersion = "3.12"
typeCheckingMode = "strict"
reportAny = "error"
reportExplicitAny = "error"
reportImplicitStringConcatenation = "error"
reportUnusedFunction = "none"

```

### packages/protocol/src/index.ts

```typescript
export {
  canonicalJSONSHA256,
  canonicalJSONStringify,
} from "./canonical.ts";
export {
  CAPTURE_EVENT_TYPES,
  type CaptureEventInput,
  type CaptureEventType,
  type CoordinationEventPayload,
  captureEventSHA256,
  type PlaneRemovePayload,
  type PlaneUpsertPayload,
  parseCaptureEvent,
  type TargetSeedPayload,
} from "./capture-event.ts";
export {
  C_ARKIT_FROM_OPENCV_ROW_MAJOR,
  type EncodedImageIntrinsics,
  projectEncodedPixelToOpenCVRay,
  RF_COORDINATE_CONVENTION,
  worldFromCameraOpenCV,
} from "./coordinates.ts";
export {
  encodeFramePacket,
  FRAME_PACKET_HEADER_BYTES,
  FRAME_PACKET_MAGIC,
  FRAME_PACKET_VERSION,
  type FramePacket,
  type FramePacketHeader,
  type FramePacketMetadata,
  parseFramePacket,
} from "./frame-packet.ts";
export {
  createFloorPlacementPreview,
  type FloorPlacementPreview,
  type FloorPlacementPreviewInput,
  PlacementPreviewInputError,
} from "./placement-preview.ts";
export {
  evaluateReplacementCover,
  ReplacementCoverInputError,
  type ReplacementCoverResult,
  type ReplacementViewCoverage,
} from "./replacement-cover.ts";
export {
  type CommitResult,
  type CommittedTransaction,
  commitProposal,
  createEmptyScene,
  type EditOperation,
  type EditOperationKind,
  type EditProposal,
  IdempotencyConflictError,
  type PlaceOperation,
  prepareProposal,
  type RemoveOperation,
  type ReplaceOperation,
  type RestoreOperation,
  RevisionConflictError,
  type SceneState,
} from "./transaction.ts";

```

### packages/catalog/src/cli.ts

```typescript
import { runIkeaCatalogOperationFromEnvironment } from "./ikea-catalog-operation.ts";
import { runIkeaIndexedSmokeFromEnvironment } from "./source-smoke.ts";

const [profile, ...additionalArguments] = process.argv.slice(2);
if (additionalArguments.length !== 0) throw new Error("catalog_profile_arguments_unsupported");

if (profile === "smoke") {
  const result = await runIkeaIndexedSmokeFromEnvironment(process.env);
  process.stdout.write(
    `${JSON.stringify({
      profile,
      productID: result.product.id,
      sourceGLBURL: result.sourceGLBURL,
      sourceContent: result.acquisition.checkpoint.content,
      preparedAssetID: result.prepared.asset.assetID,
      derivationID: result.prepared.derivationID,
      catalogID: result.proof.hit.id,
      delivery: {
        derivative: result.proof.delivery.derivative,
        sha256: result.proof.delivery.sha256,
        byteLength: result.proof.delivery.byteLength,
      },
    })}\n`,
  );
} else if (profile === "full" || profile === "incremental") {
  const result = await runIkeaCatalogOperationFromEnvironment({
    ...process.env,
    REFRAME_CATALOG_PROFILE: profile,
  });
  process.stdout.write(
    `${JSON.stringify({
      profile,
      runID: result.runID,
      status: result.status,
      configurationDigest: result.configurationDigest,
      counters: result.counters,
      reconciliation: result.reconciliation,
    })}\n`,
  );
} else {
  throw new Error("catalog_profile_must_be_smoke_full_or_incremental");
}

```

[197 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]