Follow the section matching the task and complete its verification before reporting success. Commands take a room or asset id, not a directory. <room-dir> means the resolved public or private room folder; <asset-dir> means the resolved asset folder. See data locations for lookup rules and file formats when authoring JSON.
Organize the scans, photos, models, and references the user identifies as new input. Keep project files and the demo recording video.mp4 in place. The root ignore rules keep other loose files out of Git during intake.
- Inspect each input file and match it to a room or product using the conversation and existing files. Ask when the destination is ambiguous. Continue once every file has a known destination.
- Create the relevant private folders. Move room scans/meshes to
private/rooms/<room>/scan/, room photos tophotos/, product photos toprivate/assets/<id>/photos/, and loose product GLBs toprivate/assets/inbox/. Group a glTF with its referenced buffers and images. Keep original bytes intact and resolve filename collisions without overwriting an original. - Confirm the moved files exist at their destination. Import RoomPlan JSON using the scan workflow below; adopt retailer GLBs with
fetch-amazon --file; record source URLs and known dimensions for product references. Ask for missing listed furniture dimensions before modeling a buyable item. - Update the room's
DESIGN.mdwith agreed choices and open questions. Verify the resulting room or asset, then report its location and the viewer URL.
If the user needs scanning guidance: start facing the wall to be called North, keep doors open, and export RoomPlan CapturedRoom JSON. An optional mesh must use a supported overlay format. Request straight-on photos of walls, corners, slopes, and door hinges, with a tape measure visible for key dimensions.
- The user puts the RoomPlan JSON (and an optional
.glb,.gltf,.usdzor.objmesh) into<room-dir>/scan/and the photos into<room-dir>/photos/. - Run
bun run import-scan <room> <room-dir>/scan/<file>.json. Read the WARN lines and the notes it writes intoroom.json(door swing guesses, trim, ceiling, fixtures, and any unscanned, curved, off-square or leftover walls). - Verify: run
bun run check <room>(WALLS lists every wall, its length and its cutouts) andbun run render <room>, and look at<room-dir>/renders/default--plan.png: wall names, door and window positions.
- Set
overlay.fileinroom.json, relative to the room folder, such as"scan/mesh.glb". - Run
bun run render <room> --overlay --shots plan. The plan shot hides the shell floor and cuts the mesh off above 48 in, so the scanned floor outline and lower walls show against the shell's wall bands. - Compare the mesh walls to the shell outlines. Rotate with
overlay.yawfirst (degrees CCW), then shift withoverlay.offsetx/y in inches. The px/in and origin pixel printed for the plan turn pixel errors into inches. - Check the height with a view render (
--overlay --shots <view>):offset[2]moves the mesh up. - Verify: repeat until the edges agree as closely as you can see (about 2-3 in at the plan's scale). The overlay is a reference for details. Dimensions come from
room.jsonand the tape.
- Doors: set
swing(in,out, ornonefor sliding and pocket doors) andhinge(left,right, orbothfor double doors and bifolds), seen from inside. Add closet doors and missed doors asdoorcutouts, and fix windowsillandheight. A cutout'soffsetis thesof its left edge. - Trim:
trim.baseboard(height) andtrim.casing(width), 0 = none. - Fixtures go in
room.jsonfixturesas placements, with the back (+y) against the wall and the rotation facing into the room, for example{ "id": "outlet-e", "asset": "outlet", "x": 143.8, "y": 30, "elevation": 12, "rotation": 270 }on an E wall at x = 144. Add a new fixture kind (vent, radiator) as a small parts asset. - Measure along a wall as
sfrom the photo's left corner. Write the cutout or fixture, runbun run check <room>, and compare the WALLS and FIXTURESsvalues with the photo. - Wall colors go in
materials:walls(every wall),floor,ceiling,trim, or a wall id for an accent. Each is{ "color": "#RRGGBB" }plus an optional"texture"id fromtextures/index.json; the color multiplies the texture, so use#FFFFFFfor the texture's own colors. Look up paint hex values yourself. - Sloped ceilings: edit wall
topprofiles ([[s, h], ...]) from photos and the overlay, then runbun run ceiling <room>. For shapes it can't express, hand-author the panels: a dormer or a slope in one arm of an L-shaped room shows up as SHELL warnings, but a tray, hip or vault ceiling above flat walls gives no warning at all, so compare with the photos. Use one planar polygon per flat ceiling face, with the points clockwise or counterclockwise and the edges shared exactly with neighboring panels. Wherever two levels meet at a vertical step, add a vertical riser panel, or the step leaves a see-through slot. - A roof window in a slope: a parts fixture with one thin glass
boxtilted to the slope withrotate, placed at the ceiling. check doesn't treat it as a window. - Verify: run
bun run check <room>andbun run render <room>, compare with the photos, and delete eachnotesentry you resolved.
- Get the listed W x D x H (ask the user if it's unknown).
- Save the photos to
<asset-dir>/photos/. - Write
<asset-dir>/asset.json:{ "name": "...", "source": "<product URL or photos>", "file": "parts.json", "dims": [W, D, H], "notes": "..." }. - Read the parts format. Build boxes, cylinders, lathes, or extrusions with named materials sampled from the references. Use the demo bed and demo lamp as working examples. The parts' combined bounds must match the listed dimensions within 0.5 in, with their bottom at z = 0.
- Run
bun run render --asset <id>. Fix errors and MODEL warnings, then compare all three previews with the product photos. Finish when the dimensions and orientation agree and any simplified design details are recorded in the asset'snotes.
- IKEA:
bun run fetch-ikea <URL or item no>(204.387.21works). Amazon, or any page with a 3D viewer:bun run fetch-amazon <URL>(add--id <asset-id>for non-Amazon URLs). On exit 3, give the user the manual steps: open the page in their browser, click "View in 3D", open DevTools > Network, filterglb, and save the response intoprivate/assets/inbox/. Then adopt it withbun run fetch-amazon <URL> --file private/assets/inbox/<file>.glb. If IKEA blocks every locale, usebun run fetch-amazon <IKEA product URL> --id <id>, or the userscript from github.com/apinanaivot/IKEA-3D-Model-Download-Button intoprivate/assets/inbox/plus--file. - Set
dimsinasset.jsonto the listed size and verify it against the product page, because the model is rescaled per axis to exactlydims. - Verify: run
bun run render --asset <id>and look atpreview-front.png. If it shows the back, add 180 tomodelYaw(0, 90, 180 or 270). If it shows a side, changemodelYawby 90 or 270 and swapdims[0]anddims[1]: the fetch ordered W and D to fit the model as it was turned. Re-render until the front shows. The MODEL hint says the same.
- Place, move and rotate by editing
itemsin<room-dir>/layouts/<name>.json, for example{ "id": "lamp", "asset": "demo-lamp", "x": 75, "y": 112, "elevation": 24, "rotation": 0 }. Ids must be unique across the layout's items and the room's fixtures. Branch by copying a layout file under a new name; the app's picker lists it. - Recolor parts assets with
"colors": { "<parts material name>": "#RRGGBB" }(GLB colors are ignored). Paint with layoutmaterials, which has the same keys asroom.jsonmaterials. Each key replaces the whole room entry, and wall X uses key X, elsewalls, so a room accent onNsurvives a layout that setswallsunless the layout also setsN. WALLS in check shows each wall's resolved paint and where it came from. - Save the user's pasted "Copy view" JSON as
views.<name>:"views": { "from-door": { "position": [108, 104, 64], "target": [32, 60, 30], "fov": 60 } }(plan inches, vertical fov 10-120). - The user's pasted click info (
id,point, andsfor a wall) tells you what they mean by "this" and "here". - Verify: run
bun run check <room> --layout <name>, thenbun run render <room> --layout <name>and inspect every PNG. Finish when placement, facing, and colors match the request, with errors fixed and remaining warnings explained.
-
Run
bun run check <room> --layout <name>. Fix validation and JSON errors first; geometric checks are skipped until those pass. Resolve each warning below or record why it is acceptable for this layout. Each WARNING line starts with its code:Code Meaning What to do DATADims all under 3.5 in ( looks like meters?), orcolorson a GLB asset.Convert dims to inches (x 39.37). Drop colorsfrom GLB items.OUTSIDEThe footprint pokes that far past the named wall. Move it into the room. CEILINGThe top is above the ceiling at that point (sloped ceilings). Move toward the high side, lower elevation, or choose a lower item.OVERLAPTwo footprints overlap, and so do their heights. Move one. Stacked things need elevationat or above the lower item's top.DOORIn a door's swing arc ( swing), or the 18 in clear zone of an out-swing, sliding or doorless opening (doorway).Move it out of the zone. WINDOWTaller than the sill + 2 in and within 12 in of the window. Move it, or tell the user it blocks the window. FACINGThe item's front is within 6 in of a wall. Use the rotation it names (the "0 faces North" slip). WALKWAYA 12-36 in gap between two items or an item and a wall. Widen it to 36 in, or explain (pairwise heuristic; it can flag a gap nobody walks through). MODELA parts model doesn't fill its dims box (names the axis and shift), or a GLB's bbox differs from the listed dims. Fix the parts, or verify the listed dims and modelYaw.SHELLWall tops and ceiling disagree, a cutout is taller than its wall, or floor has no ceiling over it. Run bun run ceiling <room>or fix the panels, or fix the cutout or walltop.ITEMS lists each item's walls within 36 in, such as
N 0.0 (back, s 32.0): the item's back is 0.0 in from wall N, with its center at s 32.0.(front)next to a wall means the item faces that wall: use the rotation FACING names. FIXTURES shows each fixture's nearest wall,sand gap. NOTES lists openroom.jsonnotes. -
Run
bun run render <room> --layout <name>: the plan plus every view. The plan line prints its scale (6.56 px/in, origin (SW) at px (295, 906)for the demo). -
Inspect every PNG for floating items, clipping, facing, colors, and agreement with the request. Finish only when there are zero errors, every warning is resolved or explained, and every visual mismatch is fixed or documented.
- Ask the user to tape wall to wall at about 36 in high, plus any door, window or slope start that matters.
- Compare with the WALLS lengths in
bun run check <room>. - Move only corners on the N or E side, so the SW origin stays at (0, 0).
- For each wall whose
startmoved along its own direction by delta = (new start - old start) dot dir, subtract delta from its cutoutoffsets and itstopsvalues, so they stay put in plan. - Move fixtures and items on a moved wall by the same plan shift.
- Verify: run
bun run ceiling <room>(unless the panels are hand-made), thenbun run check <room>, and confirm the WALLS cutout plan coordinates didn't change.
- Obtain the listed dimensions; ask the user if they are unavailable. Continue once all three dimensions are known.
- Copy the layout to a scratch name (
<room-dir>/layouts/<scratch>.json) and place the item there. - Run
bun run check <room> --layout <scratch>. - Answer with the gap numbers, minus 0.75 in for each baseboarded wall that the item's floor-level back or sides touch.
- Say whether the governing room dimension was tape-verified (watch for off-square wall notes). If it wasn't, ask for that one measurement before the user buys.
- Delete the scratch layout and confirm it is gone. Finish with the gap numbers, baseboard allowance, measurement confidence, and any required tape measurement in the answer.
The app produces measured 3D renders and matching .prompt.txt files. Generation uses the agent's available image tool; the app requires no image API key. If your agent has no image tool, provide the render, references, and prompt for the user to run with their preferred tool.
- Read the room's
DESIGN.md, selected layout, and relevant asset metadata. Establish the exact products, sizes, finishes, and chosen camera before generating. - Run
bun run render <room> --layout <name> --shots <view>. Inspect the PNG and read its.prompt.txt. Fix the measured scene first if furniture or geometry is wrong. - Collect actual product reference images. Follow each selected product's source URL to Amazon, IKEA, or the relevant retailer/manufacturer. Verify the exact size and color variant. Save useful front, side, detail, and material photos under that product's private asset
photos/folder; record the source URLs in its metadata or a smallreferences.md. Prefer listing images of the actual product over search thumbnails or similar-looking furniture. If a listing is blocked, try the manufacturer's page or ask for product photos; say which references are missing. - Attach the rendered scene and the downloaded reference images to the agent's image tool. Label which item each reference depicts. Use the generated prompt as the base: the render controls camera, geometry, dimensions, and placement; product photos control shape details, construction, material, and finish. Use several close-ups or separate views when one crowded reference set loses detail.
- Save the output in
<room-dir>/renders/photoreal/<layout>--<view>--<style-or-number>.png. Keep generation notes and intermediate attempts private. Press P in the viewer to browse the results. - Compare the result with the render for walls, slopes, openings, item count, placement, and scale; compare each visible product with its listing photos for design and finish. Correct mismatches and regenerate. Finish when each comparison passes or a remaining limitation is recorded beside the image. Images illustrate a design; the JSON model and tape measurements determine fit.
Prompt to adapt after attaching and labeling the images:
Turn the attached room render into a realistic interior photograph. Preserve its camera, room geometry, openings, furniture sizes, and placements. Match each named furniture item to its attached product reference photos, including its silhouette, legs, handles, upholstery, wood finish, and selected color. Preserve the measured layout while adding realistic material detail and lighting. Keep all existing items and add nothing. [List the reference-to-item mapping and any requested styling change.]
For an edit based on a real room photo, preserve the original photo in photos/, attach it with the relevant product references, and name only the requested changes. Save the generated edit separately. For example:
Edit this room photo: change the walls to sage #7C8C7A and replace the chair with the attached product. Preserve the camera, room geometry, openings, lighting, and all other furniture.
- Each product is an asset with a
productblock inasset.json(retailer, url, price, modelretailerormock, tier). See SPEC.md > Layout JSON for the layout'sshopblock: slots name the base items they replace, options give the replacement placements, presets name whole bundles. - Place each option's items yourself (back against the wall with the usual gap, same center otherwise). Items standing on a swapped item (lamps, small plants) follow it automatically.
- Verify:
bun run check <room> --layout <name> --pick <slot>:<option>for every option and every preset, thenbun run render <room> --layout <name> --pick <preset>and look.
ls private/.history/<file>/lists the versions (UTC timestamps, oldest first), for examplels private/.history/<room-dir>/layouts/<name>.json/.bun run undo <file>goes back one version (repeat to go further). It printsrestored <file> to <stamp> (<n> older versions left). Or restore by hand by copying a version fromprivate/.history/<file>/.- Verify: run
bun run check <room> --layout <name>. History only exists for changes made whilebun run devran; otherwise use your.bakcopy.
ERROR lines read <file> <json path>: <message>, for example room.json walls[1].cutouts[0].width: must be > 0. Common mistakes:
- JSON syntax:
rooms/demo/layouts/default.json:3:25: Expected "," but found ""y""gives the line and column....: trailing comma or comment (not allowed in JSON)means exactly that. - Counterclockwise walls:
room.json walls: must be listed clockwise seen from above (...). With +y North, go W wall northward, N wall eastward, and so on, with the room on the right of travel. To fix, reverse the list and start each wall at its old end; itssvalues then count from the other end (top: s -> length - s, points in reverse order; cutouts: offset -> length - offset - width). - Cutout past the end of its wall:
room.json walls[1].cutouts[0]: ends at s 152.0, past the wall's end (length 144.0).offset + widthmust be at most the wall length + 0.5. - Layout paint key naming no wall:
layouts/default.json materials.NE: no wall "NE" (walls: W, N, E, S). Rename the key to a listed wall id (a re-import or wall edit can rename walls). Inroom.jsona typo likenorthgivesunknown key. - Parts model built around its center, or the wrong size:
MODEL armchair: parts z -15.0..15.0 vs 0.0..30.0 (move every part +15.0 in z). Add the shift to every part'sat, or fix the part sizes ordims. - Dims in meters:
DATA chair: dims 0.6 x 0.6 x 0.9 in, looks like meters?. Multiply by 39.37. - GLB colors ignored:
DATA sofa: colors ignored on a GLB asset. Only parts assets can be recolored; removecolors. - check passes but the render looks wrong (an item faces a wall, or a GLB shows its side or back): fix
rotationwith the facing table, ormodelYawas in "Fetch a retailer model" step 3. - Render times out or Chromium is missing: run
bunx playwright install chromium. - Unknown asset or id clash:
items[4].asset: unknown asset "nope"means neither public nor private storage hasassets/nope/asset.json;"light" is also a room fixture idmeans pick another id.