# OpenAI + Blender MCP: the practical field guide

Version 1.0 · 2026-10-29 · An anonymized record of roughly three months of production experiments

**Purpose:** give someone a file they can attach to a fresh OpenAI conversation and use to get connected, make useful work, recover failures, and improve beyond the first plausible render.

You may copy, adapt, and share this guide. Third-party software, models, textures, and libraries retain their own licenses. This document does not distribute those assets or claim ownership of them.

## Start here: attach this file and send this message

> Read the attached guide as our working manual. Help me establish a real Blender connection, then complete a small, saved, visually reviewed project using its workflow. Inspect the tools available to this conversation before assuming you can operate my computer. Ask only the essential setup questions you cannot answer by inspection, and give me at most two or three manual steps at a time. Reuse a suitable installation. Once connected, do the authorized work, save checkpoints, inspect actual renders, fix defects, and continue through delivery. Distinguish observed results, historical experience, proposed settings, and things you have not verified. Never ask me to paste a secret. Begin with environment discovery and a read-only connection test if a Blender execution tool is available.

Uploading this Markdown file gives the assistant instructions and knowledge. It does **not** install software, create an MCP connection, grant computer access, change workspace permissions, or make tools appear automatically.

The assistant should follow this guide within the user's actual request and the platform's instructions and access controls. The guide is not permission to delete unrelated work, publish anything, send messages, disclose private files, or bypass a blocked action.

## Contents

1. [What we can actually do](#1-what-we-can-actually-do)
2. [The operating principles](#2-the-operating-principles)
3. [Identify the right Blender MCP](#3-identify-the-right-blender-mcp)
4. [Choose your connection route](#4-choose-your-connection-route)
5. [Discover the environment](#5-discover-the-environment)
6. [Install the Blender-side pieces](#6-install-the-blender-side-pieces)
7. [The settings we actually used](#7-the-settings-we-actually-used)
8. [Local Codex setup](#8-local-codex-setup)
9. [ChatGPT through a private tunnel](#9-chatgpt-through-a-private-tunnel)
10. [Persistent startup and correct restarts](#10-persistent-startup-and-correct-restarts)
11. [Prove the connection](#11-prove-the-connection)
12. [Troubleshooting without making it worse](#12-troubleshooting-without-making-it-worse)
13. [Organize a project for reliable execution](#13-organize-a-project-for-reliable-execution)
14. [The production loop](#14-the-production-loop)
15. [Run long jobs safely](#15-run-long-jobs-safely)
16. [A complete first-project exercise](#16-a-complete-first-project-exercise)
17. [Units, naming, and scene contracts](#17-units-naming-and-scene-contracts)
18. [Whole-house modeling](#18-whole-house-modeling)
19. [The realism improvement order](#19-the-realism-improvement-order)
20. [Materials and texture lessons](#20-materials-and-texture-lessons)
21. [Furniture, assemblies, and contact](#21-furniture-assemblies-and-contact)
22. [Lighting and camera lessons](#22-lighting-and-camera-lessons)
23. [Controlled experiments and regression tests](#23-controlled-experiments-and-regression-tests)
24. [Walkthroughs and motion](#24-walkthroughs-and-motion)
25. [Volumes, fire, and advanced experiments](#25-volumes-fire-and-advanced-experiments)
26. [External assets and Geometry Nodes](#26-external-assets-and-geometry-nodes)
27. [Exports, game assets, and browser delivery](#27-exports-game-assets-and-browser-delivery)
28. [Portable projects and dependable handoffs](#28-portable-projects-and-dependable-handoffs)
29. [Copyable operating prompts](#29-copyable-operating-prompts)
30. [Readiness and release checklists](#30-readiness-and-release-checklists)
31. [What this guide does not promise](#31-what-this-guide-does-not-promise)
32. [Public references and provenance](#32-public-references-and-provenance)

## 1. What we can actually do

These are capabilities reached in the source workflow, not a promise that every reader's installation already supports them.

| Capability | What was reached | Important boundary |
| --- | --- | --- |
| Natural-language control of Blender | Scene queries, Python execution, documentation searches, screenshots, rendering, and saved-file/background work through an MCP bridge | Discover the actual tools; different servers expose different surfaces |
| Parametric production | Named, reproducible models, families of assets, automated measurements, exports, and manifests | A script finishing does not prove the asset looks right |
| Whole-house scenes | Multiple house concepts with room volumes, furnishings, native scenes, room schedules, plans, exterior views, and browser exports | Visualization is not structural design, a permit set, a cost estimate, or a buildability determination |
| Architectural realism studies | Controlled work on sofas, cushions, pillow bodies and piping, bedding, wood floors, plaster, glass, furniture supports, and practical lighting | Scoped stills can pass while other rooms remain unfinished |
| Evidence-driven correction | Source/candidate geometry guards, material and lighting comparisons, sampled support checks, target-aware cameras, and deliberately broken test fixtures | Each checker has a declared scope; there is no universal “realism PASS” button |
| Native-frame house tours | A roughly eight-minute, native-30-fps tour covering 35 named spaces was produced with reusable route/capture logic | That film used an earlier furnished scene; later realism stills do not upgrade it retroactively. Framing and motion review remained separate gates |
| Animated fire studies | Gas/wood-fire volumes, 200-file sequences, and consecutive native-frame probes including sequence wrap checks | These were local experiments, not proof that a new final whole-house film was finished |
| Portable delivery | Shared texture packaging, reopen checks, native scenes, GLB candidates, images, chapter data, and website handoff packages | Geometry/import checks do not establish browser material fidelity or mobile acceptance |
| Large asset batches | Hundreds of exported assets with disk-based counts/hashes and staged delivery | Exported assets can still lack placement data or working application integration |

The strongest transferable capability is a loop: **describe a defect → make a bounded change → save → measure → inspect the real image → retain or reject → reuse the improvement.**

This is how you move past attractive one-off demos. Better prompting helps, but project organization, evidence, and persistent iteration do much of the work.

## 2. The operating principles

1. **Use Blender as the source of visual truth.** A generated illustration of a room does not prove your `.blend` scene contains that room.
2. **Prove access with execution.** A tool list, a connected badge, a running local add-on, and a passing configuration check are different pieces of evidence.
3. **Save before expensive work.** Keep scripts and checkpoints outside conversation history.
4. **Serialize edits to one scene.** One active writer owns an interactive Blender session. Independent workers use separate files and output folders.
5. **Use short orchestration calls and durable jobs.** A short tool timeout should not define how ambitious a render can be.
6. **Measure actual bytes.** Audit the saved/exported file, not only the live scene or a claimed manifest.
7. **Inspect what you intend to deliver.** Check browser exports in the browser; check films as motion; reopen portable native scenes.
8. **Keep a reviewed parent.** A pending candidate is not automatically the next approved baseline.
9. **Record failures.** Rejected images and negative tests prevent the same mistake from returning.
10. **Continue within the assignment.** Do not ask “shall I continue?” between already-authorized steps. Stop for a real blocker, a requested checkpoint, a scope decision, or an actual platform limit.

## 3. Identify the right Blender MCP

“Blender MCP” names several different projects. This matters more than it first appears.

### The setup behind our tuned workflow

Our recorded setup used **Blender Lab MCP**, with an add-on running inside Blender and a separate `blender-mcp` MCP process. Its public starting points are:

- Blender Lab: <https://www.blender.org/lab/mcp-server/>
- Source and setup documentation: <https://projects.blender.org/lab/blender_mcp>
- Lab extension repository: <https://lab.blender.org/>

The Lab page currently describes Blender 5.1 or newer as a prerequisite. Our historical connection evidence reported a 5.2.0 LTS build; that is an observation, not a requirement to obtain that exact build. Record the versions actually installed. [S1]

### The similarly named community project

The separate project at <https://github.com/ahujasid/mcp-for-blender> was formerly widely linked as `ahujasid/blender-mcp`. Its current package name and setup commands differ from the Lab project. It has its own add-on, bridge, and optional integrations. [S2]

**Do not install one project's add-on and the other project's bridge merely because both mention Blender and port 9876.** A matching port does not establish a matching wire protocol.

For a new setup following this guide, start with the Lab route. If a working community installation is already present, keep its components matched and adapt the tests rather than replacing it casually. The artistic and project-management lessons still apply.

### Capture a compatibility record

Record these locally, without secrets:

```json
{
  "blender_version": "RECORD_ACTUAL",
  "blender_binary": "RECORD_LOCAL_ABSOLUTE_PATH",
  "addon_project": "Blender Lab MCP",
  "addon_version": "RECORD_ACTUAL",
  "addon_source_revision": null,
  "mcp_server_command": "RECORD_VERIFIED_LOCAL_COMMAND",
  "mcp_server_version_or_revision": "RECORD_ACTUAL",
  "client_surface": "ChatGPT web or local Codex",
  "transport": "stdio or private tunnel",
  "addon_host": "127.0.0.1",
  "addon_port": 9876,
  "tunnel_health_port_if_used": 8081,
  "last_successful_execution_utc": null
}
```

Unknown fields stay null or explicitly unknown. Do not make up an exact version lockfile: our historical setup evolved, and this document is not a redistribution of its binaries.

## 4. Choose your connection route

| Route | Where the assistant/tool runtime lives | How it reaches Blender | Extra pieces |
| --- | --- | --- | --- |
| A: local Codex | On the computer running Blender, or an explicitly configured executor | Local MCP process → local Blender add-on | No OpenAI tunnel needed for a same-host stdio connection |
| B: ChatGPT with a private tunnel | Chat orchestration is hosted; a local tunnel client runs beside the MCP server | ChatGPT → private tunnel → local MCP process → Blender add-on | Tunnel permissions, runtime credentials, and a configured custom MCP connection |
| C: a separate render/build host | A local or remote job runner on a known machine | Saved `.blend` + scripts → isolated background Blender | Explicitly authorized file transfer/job access; separate from proving interactive MCP |

For a hosted chat, `localhost` normally refers to the machine where a particular tool executes, not automatically to your laptop. A terminal in a hosted sandbox does not imply a route into your personal computer.

A `.mcpb` bundle supported by one application does not automatically install into every OpenAI surface. A local Codex configuration does not automatically become a ChatGPT web plugin. Treat those as different setup tasks. [S3]

**Default simplification:** keep the add-on, bridge, tunnel client if needed, and project files on one machine until the first end-to-end test succeeds. Add remote machines only for an actual reason.

## 5. Discover the environment

The receiving assistant should first determine:

1. Which surface is this: hosted ChatGPT, local Codex, or another client?
2. What computer will actually run Blender, and what access is available to it?
3. Is there already an installed add-on, bridge command, tunnel profile, or managed service?

Inspect what you can. Ask only for missing facts. Do not invent shell access, claim to click UI you cannot control, or repeatedly ask the user to reconfirm evidence they just supplied.

Useful non-secret local checks on macOS/Linux:

```bash
command -v blender
command -v uv
command -v codex
command -v tunnel-client
```

An empty result can mean “not on PATH,” not “not installed.” On macOS a common Blender binary is:

```text
/Applications/Blender.app/Contents/MacOS/Blender
```

Verify that file on the intended machine. Do not hard-code a username or assume an application bundle lives in the standard location.

For GUI-launched apps and background services, use verified absolute executable paths. They may not inherit the PATH or environment variables of your Terminal.

Create a small local connection record with host roles, executable paths, profile name, ports, service label, log locations, and a restart command. Keep credentials elsewhere. That record eliminates much repeated diagnosis.

## 6. Install the Blender-side pieces

### 6.1 The add-on inside Blender

Use the current Blender Lab page and its download/setup links. The documented drag-and-drop installation has two stages: adding the Lab extension repository, then installing the add-on. Installing from disk is another path. Enable the extension and start its server in its preferences. [S1]

If Blender reports that networking/online access is disabled, inspect the relevant preference and the extension's current documentation. A local socket can still be governed by Blender's networking permissions. Change the necessary setting intentionally; do not disable unrelated security controls.

Start with a new empty project or a disposable copy. A Blender execution tool can run powerful Python code. Keep the bridge on loopback and use a trusted client. The Lab project explicitly warns that its code-execution facility is not a sandbox. [S1]

### 6.2 The separate MCP bridge

Installing the add-on is only half the job. Your MCP client needs a command that starts the matching server process.

Use the Lab project's current Setup documentation. Obtain its server from the official project/release, or reuse a verified installation. Avoid assuming that an unrelated package with the same short name is the right server.

Two common launcher forms to inspect against the installed release are:

```text
/absolute/path/to/a/managed/environment/bin/blender-mcp
```

and a source checkout launched with `uv`:

```text
/absolute/path/to/uv --directory /absolute/path/to/blender_mcp/mcp run blender-mcp
```

These are launcher patterns, not a promise about the layout of an arbitrary downloaded archive. Confirm the actual entry point and installed help. If reusing a server that another client installed, use its real executable; do not assume it is under a particular application's private directories.

The official source host was not fully retrievable during preparation of this guide. Follow its live Setup page for installation details rather than treating a guessed package-install command as validated here.

### 6.3 Check the Blender listener

On macOS:

```bash
lsof -nP -iTCP:9876 -sTCP:LISTEN
```

On Linux, if installed:

```bash
ss -ltnp 'sport = :9876'
```

On Windows PowerShell:

```powershell
Get-NetTCPConnection -LocalPort 9876 -State Listen |
  Select-Object LocalAddress,LocalPort,OwningProcess
```

Identify the process. A random process on 9876 is not Blender. If a different port is deliberately configured, use that port throughout.

Do not `curl` the add-on socket expecting an HTTP response. It is not the tunnel health server or necessarily an HTTP MCP endpoint.

## 7. The settings we actually used

The following values came from a recorded working setup and its add-on screenshot. They are a starting profile for the same family of add-on, not universal defaults or a benchmark result.

| Setting | Recorded value | Why we retained it |
| --- | --- | --- |
| Host | `localhost`, with loopback expected | Keep the local code-execution bridge local |
| Port | `9876` | Consistent add-on/bridge pairing |
| Auto Start | Enabled | Reduce missed manual starts after opening Blender |
| Auto Start Delay | `1 second` | Defer bridge start briefly during application startup |
| Active timer interval | `250 ms` / `0.25 s` | Responsive queue polling in this workflow |
| Idle timer interval | `250 ms` / `0.25 s` | Avoid the slower idle polling behavior in that profile |
| Idle delay | `1 minute` / `60 s` | Recorded transition setting; not a socket-timeout cure |
| Logging | Enabled during setup/recovery | Observe requests and failures locally |
| Tunnel health/admin address | `127.0.0.1:8081` | Avoided an observed collision on 8080; this is a separate process/port |

Historical add-on records mention different version labels on different dates. Do not call the table the default settings of every release. If your installed version omits a control, do not invent it or patch the add-on without a reason.

### What the timer tuning did not fix

We encountered an idle connection failure around a minute of inactivity. The timer changes were **not** demonstrated to eliminate it. The operational response was to:

- Prepare a short sequence of meaningful calls before beginning.
- Save scripts and files, so the connection is not the only copy of the work.
- After a pause, test the connection with a tiny read-only request before an expensive action.
- If the idle path wedges, stop repeated retries and recover the relevant add-on/bridge.
- Move long work to an isolated, durable job where possible.

Do not replace thoughtful recovery with endless keepalive calls. Reducing a polling interval does not turn Blender's main-thread operations into parallel work, solve every network timeout, or keep a sleeping laptop reachable.

### A sensible tuning experiment

Keep a baseline. Change one timing setting, record its old/new values, perform a few small real operations, deliberately pause, then test recovery. Record latency, failures, and machine load. Restore the baseline if the change is worse.

Our exact values are useful historical evidence; an improvement on your hardware should be measured rather than assumed.

## 8. Local Codex setup

This route does not require tunneling a same-machine stdio server to the public internet.

After verifying the actual executable, a registration pattern is:

```bash
codex mcp add blender -- /absolute/path/to/blender-mcp
codex mcp list
```

For a verified source-checkout launcher:

```bash
codex mcp add blender -- /absolute/path/to/uv --directory /absolute/path/to/blender_mcp/mcp run blender-mcp
```

Choose one matching registration, not both for the same instance. Local Codex supports stdio entries; its current documentation describes CLI registration and `[mcp_servers.NAME]` configuration. ChatGPT web does not read that file. [S3]

An equivalent illustrative TOML entry is:

```toml
[mcp_servers.blender]
command = "/absolute/path/to/blender-mcp"
startup_timeout_sec = 30
tool_timeout_sec = 120
```

The 30/120-second values above are proposed starting allowances, not our measured optimum. Check the installed client's help and actual upstream deadlines. Increasing a client timeout cannot extend a shorter limit in an intervening service.

Use the client's supported refresh/restart mechanism after configuration changes. Then enumerate the real tool names and perform the probe in Section 11. A saved configuration entry is not yet a successful connection.

Do not automatically add powerful filesystem/process permissions just because the agent requests them. If the current execution policy cannot support background subprocesses, use the supported saved-file tools or a separately authorized local runner instead.

## 9. ChatGPT through a private tunnel

### 9.1 Understand the layers

There are separate components:

| Component | Job | What it does not prove |
| --- | --- | --- |
| Blender add-on | Executes operations in Blender | Hosted chat can reach the machine |
| Matching MCP bridge | Translates MCP tools into Blender operations | OpenAI's tunnel is connected |
| Local `tunnel-client` | Carries requests between OpenAI and the private MCP target | Every request reached Blender |
| ChatGPT custom MCP plugin/connection | Makes configured tools available in the conversation | The local process is still running |

The OpenAI private tunnel uses an outbound client near the MCP server. Availability depends on the supported product surface and organization/workspace permissions; this guide does not grant them. [S4]

### 9.2 Prepare the account and runtime

Use the current official tunnel guide and Platform UI. Reuse a suitable existing tunnel. Obtain its identifier and an appropriate runtime key locally. The tunnel and intended ChatGPT workspace must be correctly associated. Managing tunnels and running them require different permissions. [S4, S5]

Keep these concepts separate:

- **Tunnel identifier:** identifies a routing resource; it is not an API key.
- **Runtime credential:** authenticates the local client; keep it out of chat.
- **MCP server authentication:** a separate question from tunnel runtime authentication.

Do not solve discovery errors by randomly switching OAuth settings. Select the authentication scheme your actual MCP server supports.

### 9.3 Discover the installed CLI before using old flags

```bash
tunnel-client help quickstart
tunnel-client help doctor
```

Use the verified absolute binary path if it is not on PATH. A historical failure in our setup was simply that a bare command was not discoverable.

### 9.4 Create a named stdio profile

The following is a **template for a new profile**, using the current documented command family. Replace values before running; inspect/reuse existing profiles instead of overwriting them.

```bash
tunnel-client init \
  --profile blender-local \
  --sample sample_mcp_stdio_local \
  --tunnel-id YOUR_ACTUAL_TUNNEL_ID \
  --mcp-command '/absolute/path/to/blender-mcp'
```

The target is the MCP bridge command, not the Blender application and not `http://localhost:9876`. If the launcher has arguments, preserve them using the installed CLI's documented command parsing. An executable-only wrapper around a verified command can avoid nested quoting mistakes. [S6]

Do not hand-author a YAML schema from memory. Let the installed CLI generate its supported profile, then inspect non-secret target/port fields.

### 9.5 Load a credential without sharing it

Our historical working path sourced an existing local environment file. A file is convenient, but it is plaintext, not a password vault. Prefer an appropriate OS-backed secret store for unattended deployments when supported. Never put a secret in this public guide, a command argument, a transcript, or a shared `.blend` text block.

If you intentionally choose a local environment file, this optional Python snippet prompts locally and refuses to overwrite an existing file:

```python
import getpass
import os
import shlex
from pathlib import Path

target = Path.home() / ".config/tunnel-client/blender-local.env"
target.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
if target.exists():
    raise SystemExit("Existing credential file kept; use its established loader.")
key = getpass.getpass("Runtime tunnel key, entered locally: ").strip()
if not key:
    raise SystemExit("No key entered; no file created.")
fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
    handle.write("CONTROL_PLANE_API_KEY=" + shlex.quote(key) + "\n")
print("Created the local credential file; its contents were not printed.")
```

Save/run that code locally as a Python script. If using a heredoc, the secure prompt still needs a usable local terminal. Do not run it through a remote chat tool that will capture the key.

### 9.6 First successful foreground run

Use Bash for this snippet, and substitute the actual verified binary path. The 8081 choice is an example retained from our working recovery, not a reserved port.

```bash
(
  set +x
  set -a
  source "$HOME/.config/tunnel-client/blender-local.env" || exit
  set +a
  /absolute/path/to/tunnel-client doctor \
    --profile blender-local \
    --health.listen-addr 127.0.0.1:8081 --explain &&
  /absolute/path/to/tunnel-client run \
    --profile blender-local \
    --health.listen-addr 127.0.0.1:8081
)
```

This is intentionally the **foreground test**, not a persistent installation. Keep its Terminal attached while testing. Do not start it if a healthy managed runtime for the same target already exists.

In another terminal:

```bash
curl --noproxy '*' --max-time 5 -sS -i http://127.0.0.1:8081/readyz
```

Check the local admin UI if supplied by your version. A stdio readiness check can succeed before protocol discovery is observed; end-to-end tool execution remains the final connection test. [S5]

### 9.7 Add the custom MCP connection in ChatGPT

Current official instructions describe adding a custom MCP server from ChatGPT's Plugins UI, choosing a server URL or a tunnel, configuring authentication, and installing the resulting plugin before use. UI labels can change; consult the linked page rather than searching indefinitely for an older “developer mode” toggle. Workspace policy still applies. [S7]

Select the **same tunnel** the local runtime uses. Refresh/discover its tools and enable the needed tools in the intended conversation. Run Section 11's read-only probe. Do not publish a private tunnel as a public plugin listing: private testing and public distribution have different endpoint requirements. [S4]

## 10. Persistent startup and correct restarts

### 10.1 First inventory, then repair

Before creating anything persistent, identify the executable, arguments, profile, service label, credentials loader, logs, and health listener of existing jobs.

The name “tunnel” is weak evidence. In one real incident, a supposedly relevant persistent tunnel job was actually `/usr/bin/ssh`, forwarding unrelated application ports. Blender was listening, but no local OpenAI tunnel client was running. Restarting the SSH job would not start that missing process.

### 10.2 Prefer the current client's supported lifecycle

Newer `tunnel-client` documentation describes managed `runtimes` operations. If your installed version supports them, inspect `runtimes connect --help`, configure the verified target and credential reference, and use its status output. Do not improvise `nohup`/`disown` or add another supervisor over an already-managed instance. [S6]

Useful discovery/status patterns:

```bash
tunnel-client runtimes list
tunnel-client runtimes connect --help
tunnel-client runtimes status blender-local --json
```

Do not assume these commands exist in an older binary. A process being launched, healthy, ready, and able to execute Blender are four distinct states.

### 10.3 Existing macOS launchd installations

Our workflow also used launchd-based supervision in related infrastructure. A Mac-specific repair installer for the Blender client was prepared and locally logic-tested during one incident; its installation on that Mac was **not confirmed** in the evidence used for this guide. This guide therefore does not call that proposed repair a proven permanent Blender fix.

For an intentionally configured user LaunchAgent, use your own label and current user ID:

```bash
launchctl print "gui/$(id -u)/org.example.blender-tunnel"
launchctl kickstart -k "gui/$(id -u)/org.example.blender-tunnel"
```

`org.example.blender-tunnel` is a placeholder. Run the restart only after confirming that exact job is the Blender tunnel you intend to restart. A restart is not needed on every session.

For an installed job not yet registered, the relevant operation can be:

```bash
launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/org.example.blender-tunnel.plist"
```

An agent's usual design is `RunAtLoad`, `KeepAlive`, a measured restart throttle, absolute paths, private logs, and an `exec` wrapper that loads its credential locally. User agents run in a login session; they do not make a sleeping or logged-out laptop an always-on render server. [S8]

If an assistant prepares a new service, require it to inspect conflicts, avoid duplicate clients, preserve existing files, validate the target, provide uninstall instructions, and verify after Terminal closes and after a normal login/restart. Do not add persistence until the foreground path works.

### 10.4 Inspect an SSH alias without guessing

On a system that actually uses an SSH bridge, this read-only pattern shows resolved non-secret forwarding settings:

```bash
ssh -G YOUR_HOST_ALIAS |
  grep -E '^(hostname|user|port|localforward|remoteforward|serveraliveinterval|serveralivecountmax) '
```

Review the output locally before sharing host/user details. `ExitOnForwardFailure` does not make SSH exit merely because a later forwarded destination refuses a connection. A running SSH process is not proof that an application behind it works. [S9]

On Linux or Windows, use the platform's intended service manager/task mechanism if needed. Do not paste macOS `launchctl` commands into another OS, and do not claim this guide provides a tested installer for all three platforms.

## 11. Prove the connection

Ask the client to discover the actual Blender tools. In the Lab tool surface used here, the interactive execution tool is named `execute_blender_code`; a saved-file/background counterpart may be named `execute_blender_code_for_cli`. Tool names can be namespaced by the host product.

### 11.1 One read-only execution probe

Supply a fresh harmless probe label in the request so an old pasted result cannot be confused with a new execution:

```python
import bpy
import json

result = {
    "ready": True,
    "probe": "REPLACE_WITH_A_FRESH_TEST_LABEL",
    "blender_version": bpy.app.version_string,
    "background": bpy.app.background,
    "scene_count": len(bpy.data.scenes),
    "object_count": len(bpy.data.objects),
    "saved_file": bool(bpy.data.filepath),
}
print(json.dumps(result))
```

Our Lab wrapper returned a JSON-serializable `result`. Other bridges may capture stdout instead; that is why the example also prints it. Follow the installed tool's schema rather than insisting all forks have the same return convention.

The test does not create or delete objects. An empty file path on a newly opened file means “not yet saved,” not “all old projects disappeared.”

### 11.2 Report the right evidence

| Observation | Honest statement |
| --- | --- |
| Blender add-on says running | “The local add-on reports running.” |
| Port 9876 is listening | “The local socket has a listener; owner checked separately.” |
| `doctor` passes | “Configuration/startup checks passed within that doctor's scope.” |
| `/readyz` returns 200 | “The tunnel runtime's readiness policy passed.” |
| Tools are listed | “Discovery returned these tools.” |
| The fresh probe returns through ChatGPT | “This conversation executed this read-only code in Blender at this time.” |
| A native file is saved and reopened | “This saved artifact was verified from disk.” |

Do not collapse the rows into one “connected” claim.

### 11.3 The first writable proof

Once the user has authorized a new scratch project, create a small object in that isolated scene, save `.blend`, export GLB, render a PNG, and inspect all three. That tests a much more useful path than a text-only health response.

Keep existing user work intact. Section 16 provides an isolated background exercise; it does not require clearing the interactive scene.

## 12. Troubleshooting without making it worse

### 12.1 The decision table

| Symptom | First interpretation to check | Useful next action | Avoid |
| --- | --- | --- | --- |
| Command not found | Wrong PATH or executable location | Locate the real executable; use its absolute path | Reinstalling everything immediately |
| Missing runtime key | Service/shell did not load the local secret | Fix the established credential loader locally | Pasting the key into chat |
| Address already in use | Another process owns that exact port | Identify owner; select a deliberate free loopback health port or restart the confirmed relevant service | Killing a random listener |
| Blender listens on 9876, health port absent | Add-on and tunnel client have different states | Check the actual tunnel process/service | Repeatedly toggling Blender |
| Tools list, execution fails | Discovery and execution paths differ | Read error; inspect bridge target and Blender listener | Declaring success from the tool list |
| Error immediately after a long idle | Possible idle socket issue | One tiny probe; recover the known relevant add-on/bridge if it wedges | Repeated full build attempts |
| Timeout during render/export | Blender may still be busy | Inspect job/process and output receipts before retrying | Launching a duplicate job |
| Tunnel HTTP 429/404 before execution | Upstream routing, rate, association, or availability issue may be involved | Preserve exact response, observe supplied retry delay, inspect the client, make a bounded retry after recovery/cooldown | Calling it scene corruption or claiming an exact cause without evidence |
| Black render | Wrong camera, occluder, missing light/texture, or real dark room | Verify evaluated camera pose and first-hit geometry; inspect actual image | Immediately multiplying all light powers |
| Missing local screenshot path | File does not exist where the reader tried to open it | Re-render/recover and transfer the actual image into the reader's environment | Calling it an MCP failure solely from the file error |
| Blender process crash | Application/build/scene-specific failure | Preserve crash log and minimal reproducer; recover from saved parent | Assuming transport tuning will fix the renderer |
| Access/approval refusal | An actual execution boundary | Report action/reason; use a genuinely narrower permitted alternative | Encoding or rerouting the same prohibited action |

### 12.2 Busy Blender versus a dead connection

This distinction saved a great deal of wasted work.

- **Idle wedge:** a tiny request fails after a pause; no known render/build is running. Stop retrying the heavy action. Recover the affected connection component and prove it with one small test.
- **Busy operation:** a render, remesh, large import, or export is in progress. Blender's main thread may not answer another request. Check the durable job record and exact process. Wait for completion or a real failure.
- **Crash:** the process exited or there is an application crash record. Preserve evidence and open a saved checkpoint.

Neither “every timeout means dead” nor “every timeout means still working” is safe. We saw timed-out operations leave real rendered files, and other timed-out writes whose outcome was uncertain. Inspect the intended result; do not assume either outcome.

### 12.3 A small macOS diagnostic bundle

Run on the computer that should host the client and Blender:

```bash
pgrep -l tunnel-client
lsof -nP -iTCP:9876 -iTCP:8081 -sTCP:LISTEN
curl --noproxy '*' --max-time 5 -sS -i http://127.0.0.1:8081/readyz
```

Substitute your actual ports. Port ownership visibility can be limited by user permissions; no `lsof` output from another account is not proof that no other user's listener exists.

Do not dump all environment variables, full secret-bearing configurations, or command arguments that might contain credentials. Prefer narrowly selected fields and reviewed/redacted logs.

### 12.4 The missing-image trap

An error like:

```text
Could not read local image at /workspace/session/upload/frame.png:
No such file or directory
```

means a file read failed at that path. Possible causes include transient workspace cleanup, a stale attachment reference, a file created only on the Blender host, or a screenshot that was never transferred.

If a tool returns an actual image, inspect that returned image. If it returns only a remote path, transfer the bytes through an authorized route before using a local image viewer. Regenerate a missing disposable proof; recover an important saved artifact from durable storage. Never invent a successful visual review of an unread image.

An error recurring every few minutes can be a repeated attempt to read the same missing file. That timing alone does not prove an automation, a tunnel failure, or a Blender crash.

### 12.5 A naming crash we encountered

One recorded Blender build crashed during an integer/name-suffix parsing path (`stoi`/name-number splitting). Simplifying generated names and replacing period separators was part of the successful recovery in that case.

Transferable practice: use short semantic names, stable IDs in custom properties, and conservative separators such as underscores. Keep a minimal reproducer for a crash. Do not claim every period-containing name is invalid or that every Blender release shares the defect.

## 13. Organize a project for reliable execution

Suggested structure, using a fictional project:

```text
studio-project/
  START-HERE.md
  brief.md
  environment.json
  contracts.json
  state.json
  NEXT.md
  manifest.jsonl
  reviewed-scenes.json
  scripts/
  sources/
  scenes/
  exports/
  proofs/
  experiments/
  jobs/
  packages/
  assets/
    asset-register.csv
    textures/
    licenses/
```

This is an example directory list, not a required tool installation layout.

### Minimum durable state

```json
{
  "project": "fictional-study-room",
  "status": "IN_PROGRESS",
  "active_assignment": "Build and review one furnished study room",
  "reviewed_parent": {
    "path": "scenes/room-r003.blend",
    "sha256": "REPLACE_WITH_MEASURED_HASH"
  },
  "pending_candidate": null,
  "active_job": null,
  "completed": [],
  "ready_next": "Render the existing material candidate from the fixed review camera",
  "blocked": [],
  "last_verified_connection_utc": null,
  "current_usage_reading": null
}
```

Use a consistent schema and update it after meaningful work. A state file should explain the next action to a fresh assistant without a transcript archaeology project.

### Keep work status precise

Use statuses such as:

- `PLANNED`: scoped but not built.
- `BUILT`: output exists.
- `RENDERED_AWAITING_REVIEW`: images exist, visual decision pending.
- `REJECTED`: evidence retained; do not promote.
- `REVIEWED_WORKING_COPY`: passed its stated scope.
- `EXPORTED`: an export was written.
- `VERIFIED`: specified disk/import checks passed.
- `DELIVERED`: destination bytes were confirmed.
- `INTEGRATED`: consumer references/configuration were changed and checked.
- `RELEASED`: the required final acceptance happened.

Do not let a filename such as `final-final.blend` substitute for status evidence.

## 14. The production loop

### Before modeling

1. Convert the brief into a complete output roster: filenames, units, dimensions, camera views, performance constraints, dependencies, and destination.
2. Inspect actual references. If a required host/socket/reference is missing, mark affected work blocked rather than inventing a substitute.
3. Identify protected geometry, existing objects, and the reviewed parent.
4. Make the smallest saved proof that exercises the intended delivery path.

### For each meaningful change

1. State the defect or design goal.
2. Save or confirm the parent checkpoint.
3. Run one coherent modeling operation or isolated job.
4. Save a new candidate.
5. Check dimensions, identities, dependencies, and required technical invariants.
6. Render or capture the relevant views.
7. Open the actual image and describe the defect if one remains.
8. Correct and repeat.
9. Record the acceptance scope, artifact hashes, and next step.

For a substantial layout, do at least three review-and-correct rounds. Do not satisfy this by generating three nearly identical screenshots without decisions.

### Keep calls efficient

- Return compact measurements and paths, not enormous scene dumps.
- Filter by explicit collections/roles/names before requesting object details.
- Separate a quick status call from a large render.
- Persist a long script first; do not repeatedly transmit and re-execute an unfinished giant program.
- Older fragile sessions benefited from writing scripts in small verified chunks. That was a transport workaround, not a universal 25-line artistic limit.
- Prefer direct data API operations when context-sensitive `bpy.ops` would be brittle. When operators are needed, set the intended context explicitly.

### What “keep working” should mean

An assistant should finish the already-authorized ready work, then proceed to the next ready item. It should not voluntarily stop after the first pass, impose an arbitrary two-item limit, or pause merely to ask for the same authorization again.

It should also not pretend a chat turn is a guaranteed unattended service. Real background work requires a real running job with saved state. Account usage, resets, and platform limits are not controllable by writing “run forever” into a prompt.

## 15. Run long jobs safely

### The preferred division of labor

Use MCP for discovery, small edits, launching supported work, and inspecting results. Use isolated background Blender for expensive builds, controlled renders, audits, and batch exports when the available execution policy supports it.

Typical job command after checking the installed Blender's help:

```bash
"$BLENDER_BIN" --background "$SOURCE_BLEND" --disable-autoexec \
  --python-exit-code 2 --python "$JOB_SCRIPT" -- \
  --config "$JOB_CONFIG"
```

`--disable-autoexec` is not a sandbox: the explicitly supplied job script still executes. Use files and scripts you intend to run. Command ordering matters; the script should apply and record settings after loading its intended source.

On macOS, set `BLENDER_BIN` to the verified app binary. On other systems use the actual installed executable. Do not infer a background binary path from the fact that the GUI is open.

### A durable job should record

- Job ID and UTC start time.
- Machine role and exact process ID.
- Source file path and SHA-256.
- Script/config hashes.
- Command structure with secrets omitted.
- Expected outputs and log path.
- Exit code, completion timestamp, and artifact hashes.
- Technical checks and a separate visual-review status.

Write a final receipt only after outputs are closed. An atomic temporary-file replacement helps avoid truncated JSON, but does not make a collection of many files transactional.

### Keep the scene and the job independent

Do not reset another person's interactive scene to create a private worker. Do not run two writers against the same candidate file. Give each candidate a new filename and each render recipe a new output directory.

For an unresponsive job, check its **exact** process and receipt. A PID alone can be reused later, and an old image file may predate the current attempt. Do not “recover” by killing every Blender process.

### Laptop sleep

Long jobs need an awake, powered host. On macOS, a time-bounded `caffeinate` invocation can help an intentionally supervised run, for example:

```bash
caffeinate -dimsu -t 14400
```

That is a four-hour example, not an always-on deployment. Lid/power/thermal behavior and OS policy still matter. Save work even when sleep prevention is active.

### Deferred tools are implementation-specific

The Lab interactive surface used here supported a deferred-completion mechanism for some requests. Its background mode did not support the same mechanism. Do not assume every tool accepts a `check_is_finished` pattern, and do not assume a background saved-file call automatically saves changes.

Inspect the schema and explicitly save required changes. A tool returning success with no `save_mainfile` operation may leave the original disk file unchanged.

## 16. A complete first-project exercise

This exercise is deliberately modest: create a furnished test room, render it, save it, and export it. It demonstrates a reliable pipeline, not the final realism standard.

Ask the assistant to save the following as `first_room.py` on the intended Blender host. Run it **only in a new background worker**. It refuses interactive use and refuses a nonempty output folder.

```python
import hashlib
import json
import os
from pathlib import Path

import bpy
from mathutils import Vector

if not bpy.app.background:
    raise RuntimeError("This exercise requires a private background Blender process.")

out = Path(os.environ["BLENDER_MCP_DEMO_DIR"]).expanduser().resolve()
out.mkdir(parents=True, exist_ok=True)
if any(out.iterdir()):
    raise RuntimeError("Choose a new empty output directory; existing files are kept.")

bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.unit_settings.system = "METRIC"
scene.unit_settings.scale_length = 1.0
scene.render.engine = "CYCLES"
scene.cycles.device = "CPU"
scene.cycles.samples = 32
scene.render.resolution_x = 960
scene.render.resolution_y = 720
scene.render.resolution_percentage = 100
scene.render.image_settings.file_format = "PNG"
scene.view_settings.view_transform = "AgX"

def material(name, color, roughness):
    mat = bpy.data.materials.new(name)
    mat.use_nodes = True
    shader = mat.node_tree.nodes.get("Principled BSDF")
    shader.inputs["Base Color"].default_value = (*color, 1.0)
    shader.inputs["Roughness"].default_value = roughness
    return mat

plaster = material("Wall_Plaster", (0.72, 0.69, 0.62), 0.8)
wood = material("Floor_Wood_Proxy", (0.22, 0.105, 0.045), 0.42)
fabric = material("Sofa_Fabric_Proxy", (0.12, 0.19, 0.18), 0.85)
dark = material("Furniture_Dark", (0.025, 0.028, 0.025), 0.4)

def box(name, center, size, mat, bevel=0.0):
    bpy.ops.mesh.primitive_cube_add(size=1, location=center)
    obj = bpy.context.object
    obj.name = name
    obj.dimensions = size
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    obj.data.materials.append(mat)
    obj["role"] = "furniture" if "Sofa" in name or "Table" in name else "architecture"
    if bevel:
        mod = obj.modifiers.new("Edge_Bevel", "BEVEL")
        mod.width = bevel
        mod.segments = 3
    return obj

# Coordinates are metres; the room is 5 by 4 metres with a 3-metre ceiling.
box("F00_Study_Floor", (0, 0, -0.06), (5.3, 4.3, 0.12), wood)
box("F00_Study_LeftWall", (-2.575, 0, 1.5), (0.15, 4.3, 3), plaster)
box("F00_Study_RightWall", (2.575, 0, 1.5), (0.15, 4.3, 3), plaster)
box("F00_Study_Ceiling", (0, 0, 3.075), (5.3, 4.3, 0.15), plaster)
# Back wall has a real opening, not a glass object embedded in a solid wall.
box("F00_Study_BackLeft", (-1.85, 2.075, 1.5), (1.3, 0.15, 3), plaster)
box("F00_Study_BackRight", (1.85, 2.075, 1.5), (1.3, 0.15, 3), plaster)
box("F00_Study_WindowSillWall", (0, 2.075, 0.45), (2.4, 0.15, 0.9), plaster)
box("F00_Study_WindowHeader", (0, 2.075, 2.75), (2.4, 0.15, 0.5), plaster)
# Front wall leaves an unobstructed entrance.
box("F00_Study_FrontLeft", (-1.525, -2.075, 1.5), (1.95, 0.15, 3), plaster)
box("F00_Study_FrontRight", (1.525, -2.075, 1.5), (1.95, 0.15, 3), plaster)
box("F00_Study_DoorHeader", (0, -2.075, 2.55), (1.1, 0.15, 0.9), plaster)

# Obvious furniture-scale proxies, intentionally ready for a second modeling pass.
box("F00_Study_SofaSeat", (-1.72, 0.25, 0.48), (0.95, 1.95, 0.24), fabric, 0.045)
box("F00_Study_SofaBack", (-2.15, 0.25, 0.85), (0.18, 1.95, 0.68), fabric, 0.04)
for y in (-0.62, 1.12):
    box("F00_Study_SofaArm", (-1.72, y, 0.68), (0.95, 0.2, 0.5), fabric, 0.04)
for x in (-2.05, -1.4):
    for y in (-0.45, 0.95):
        box("F00_Study_SofaFoot", (x, y, 0.18), (0.07, 0.07, 0.36), dark, 0.005)
box("F00_Study_TableTop", (-0.2, 0.25, 0.48), (0.8, 1.1, 0.06), wood, 0.015)
for x in (-0.5, 0.1):
    for y in (-0.18, 0.68):
        box("F00_Study_TableLeg", (x, y, 0.225), (0.05, 0.05, 0.45), dark, 0.004)

world = bpy.data.worlds.new("World_Test")
world.use_nodes = True
world.node_tree.nodes["Background"].inputs["Color"].default_value = (0.7, 0.8, 1.0, 1.0)
world.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.25
scene.world = world

def aim(obj, target):
    obj.rotation_euler = (Vector(target) - obj.location).to_track_quat("-Z", "Y").to_euler()

light_data = bpy.data.lights.new("Window_SoftLight", "AREA")
light_data.energy = 600
light_data.shape = "RECTANGLE"
light_data.size = 2.2
light_data.size_y = 1.5
light = bpy.data.objects.new("Window_SoftLight", light_data)
scene.collection.objects.link(light)
light.location = (0, 3.1, 2.1)
aim(light, (0, 0, 1.0))

camera_data = bpy.data.cameras.new("F00_Study_EntranceCamera")
camera = bpy.data.objects.new("F00_Study_EntranceCamera", camera_data)
scene.collection.objects.link(camera)
camera.location = (0.25, -1.8, 1.55)
camera_data.lens = 22
camera_data.clip_start = 0.02
aim(camera, (-0.4, 0.65, 1.3))
scene.camera = camera
bpy.context.view_layer.update()

scene.render.filepath = str(out / "entrance.png")
bpy.ops.wm.save_as_mainfile(filepath=str(out / "study-room-r001.blend"))
bpy.ops.render.render(write_still=True)
bpy.ops.export_scene.gltf(filepath=str(out / "study-room-r001.glb"),
                          export_format="GLB", export_cameras=False,
                          export_lights=False, export_animations=False)

artifacts = []
for name in ("study-room-r001.blend", "study-room-r001.glb", "entrance.png"):
    path = out / name
    if not path.is_file() or path.stat().st_size == 0:
        raise RuntimeError("Required output missing or empty: " + name)
    artifacts.append({"file": name, "bytes": path.stat().st_size,
                      "sha256": hashlib.sha256(path.read_bytes()).hexdigest()})
(out / "receipt.json").write_text(json.dumps({
    "status": "BUILT_AND_RENDERED_AWAITING_VISUAL_REVIEW",
    "blender_version": bpy.app.version_string,
    "unit_scale_metres_per_bu": scene.unit_settings.scale_length,
    "artifacts": artifacts,
}, indent=2), encoding="utf-8")
print("Saved exercise outputs to", out)
```

Example Bash invocation after saving the script at the indicated local path:

```bash
BLENDER_BIN="/Applications/Blender.app/Contents/MacOS/Blender"
BLENDER_MCP_DEMO_DIR="$HOME/BlenderProjects/mcp-first-room" \
  "$BLENDER_BIN" --background --factory-startup --disable-autoexec \
  --python-exit-code 2 --python "$HOME/BlenderProjects/first_room.py"
```

Use the correct binary on your OS. This script targets the contemporary Lab-era Blender API and has been syntax-checked for this guide; it was **not** rendered on every recipient platform during preparation.

### Review it in three rounds

1. **Round 1 — spatial:** inspect the entrance view, a top view, and an opposite corner. Check wall openings, furniture scale, floor contact, camera pose, and missing geometry. Save corrections as a new revision.
2. **Round 2 — form/material:** improve the proxy sofa geometry, choose plausible fabric and wood scale, add window/frame detail, and compare the same cameras. Save and reopen.
3. **Round 3 — presentation:** fix lighting and composition, inspect a close view and wide view, then reopen/export the selected revision and view the GLB in its consumer.

The receipt intentionally says “awaiting visual review.” Its existence is not a claim the image was seen or the room is photorealistic.

## 17. Units, naming, and scene contracts

### 17.1 Choose one coordinate convention

Two conventions worked in different projects:

| Convention | `scale_length` | One coordinate unit represents |
| --- | --- | --- |
| Metre geometry | `1.0` | One metre |
| Foot geometry | `0.3048` | One foot |

Setting a units label is not the same as rescaling existing mesh coordinates. Importers, exporters, modifiers, lights, and scripts need to agree on the convention.

For a scene whose scale is established:

```python
import bpy

metres_per_bu = bpy.context.scene.unit_settings.scale_length
if metres_per_bu <= 0:
    raise RuntimeError("Invalid scene unit scale")

def metres_to_bu(value):
    return value / metres_per_bu

def bu_to_metres(value):
    return value * metres_per_bu

def square_bu_to_square_feet(area):
    return area * metres_per_bu ** 2 / 0.3048 ** 2
```

Measure an imported known-size object and an exported/reimported known-size object. Do not accept a visually similar miniature as a successful unit conversion.

### 17.2 Name semantically

Examples:

```text
F00_Living_Wall_North
F00_Living_Window_W01
F00_Living_Sofa_Seat_01
F01_BedroomA_RoomVolume
F01_BedroomA_Pillow_Piping_01
SITE_Terrain_Main
CAM_F00_Entry_To_Rear
```

Use custom properties such as `room_id`, `floor_id`, `role`, `assembly_id`, and `source_asset_id`. A name is useful for discovery but is not a reliable substitute for explicit roles.

One bad census treated walls containing “Bedroom” and suspended curtains as unsupported furniture. Classification needs more than substring matching and low bounding-box heights.

### 17.3 Protect what must not change

Before a material or furniture pass, retain:

- Source scene hash and expected parent identity.
- Unit scale and origin convention.
- Room identity and required dimensions.
- Protected walls, floors, roofs, openings, and site geometry.
- Required cameras or explicit camera recipes.
- Existing material graphs/images/bindings when not part of the experiment.
- Lighting, display transform, exposure, and relevant render visibility.

Check both raw geometry and evaluated geometry where modifiers or transforms matter. A static frame-1 audit does not protect an entire animated route.

## 18. Whole-house modeling

### 18.1 Build the spatial program first

Start with exterior massing, floor plates, roof forms, and named room volumes. Add openings and furniture-scale blocks after the layout makes sense. Then develop realistic detail.

Choose floor count and total area based on the brief. Explain the tradeoff in plain language. A larger house is not automatically a more efficient design.

Translate requirements into measurable checks:

- Room dimensions and usable areas.
- Bedroom floor grouping and attached-bath adjacency.
- Office separation and actual exterior windows.
- Kitchen work-zone clearance and structural-placeholder exclusions.
- Hall sizes, total circulation, and the denominator used.
- Clear sightlines and usable path widths.
- Window/roof/skylight placement.
- Floor-to-ceiling heights and model unit scale.

### 18.2 Room schedules must reconcile

Specify whether totals are net room area, gross conditioned area, garage area, covered exterior area, or an all-inclusive footprint. Do not double-count overlapping room volumes. Keep walls, shafts, stairs, and voids explicit.

Our reviews exposed a circulation result that passed against a gross denominator and failed against net area. Report both where relevant; do not quietly choose the larger denominator to improve a percentage.

A useful schedule row is:

```json
{
  "room_id": "F00_STUDY",
  "floor": "F00",
  "name": "Study",
  "dimensions_basis": "clear interior",
  "length_ft": 12,
  "width_ft": 10,
  "area_sq_ft": 120,
  "area_method": "rectangle",
  "circulation_category": "room",
  "review_status": "PENDING"
}
```

For irregular rooms, measure the actual boundary rather than multiplying bounding-box dimensions.

### 18.3 Inspect the right views

For each major stage, review a top view per floor, four exterior corners, and key interior sightlines. A tall foyer or impressive roof can look good from one angle while concealing a missing wall on the opposite side.

For a front-to-back sightline, check the actual path corridor and objects that can obstruct it. A clear mathematical centerline is weaker than a usable corridor of declared width. Door leaves, furniture, glazing bars, and level changes may matter.

Place skylight glazing in the actual roof plane with plausible visual framing. Floating a rectangle above the roof is not a completed roof opening.

### 18.4 Construction-looking detail without engineering claims

Model visible thickness, reveals, trim, sill depth, roof edges, flashings as appropriate to the visualization, baseboards, and openings. Keep any dimension estimates labeled.

If working from drawings, distinguish authoritative dimensions from inferred photo-based appearance. If the drawings are incomplete, keep the unknowns visible in the report.

Do not infer structural adequacy, code compliance, cost, weather-tightness, or buildability from a convincing image.

## 19. The realism improvement order

Work in this order unless a particular defect is more urgent:

1. Correct scale, proportions, layout, and missing/penetrating geometry.
2. Correct the major silhouettes and assembly relationships.
3. Establish believable support/contact and useful light paths.
4. Set a coherent camera and display/exposure baseline.
5. Give the major surfaces plausible finish and texture scale.
6. Develop furniture form, upholstery, joinery, glazing, trim, and hardware.
7. Add coherent outdoor depth and context.
8. Add small lived-in details with restraint.
9. Increase samples/resolution to resolve a specific remaining question.
10. Review motion and browser delivery separately.

More polygons and more samples did not fix our oversized sofa seats, periodic cushion ridges, floating supports, or blocked cameras. Texture quality did not make flat pillow slabs look full.

### Suggested release rubric

This is a proposed review framework, not a scientific realism probability:

| Dimension | Weight | What to inspect |
| --- | --- | --- |
| Form/construction appearance | 25% | Scale, silhouette, thickness, believable joins, complete surfaces |
| Materials | 25% | Mapping, finish, color, roughness, repetition, geometry interaction |
| Light | 20% | Readable rooms, plausible sources, glare, window behavior, shadow quality |
| Contact | 10% | Feet, bedding, trims, fixtures, props, support hierarchy |
| Environment | 10% | Near-ground depth, context, parallax, vegetation and horizon coherence |
| Camera/composition/temporal | 10% | Framing, distortion, clearance, transitions, motion stability |

Score 0 broken, 1 placeholder, 2 developed CG with obvious giveaways, 3 credible for the target view, 4 convincing under demanding comparison. One proposed gate is every applicable category at least 3, weighted mean at least 3.5, and zero critical defects. Keep unreviewed categories null. The user makes final acceptance decisions.

## 20. Materials and texture lessons

### 20.1 Material realism is not a download count

For each important finish, inspect a wide view and close view. A map can look impressive on a shader ball and be wrong across a whole room.

Record texture provenance, intended physical scale, mapping source, image color spaces, material graph, and material-level render settings that affect appearance. Color textures and data maps serve different roles; inspect the asset's own documentation and the installed Blender shader interface.

Do not blindly overwrite a material because `uv_layers` is empty. We imported vegetation whose shader read a named CORNER/FLOAT_VECTOR attribute called `UVMap`; its usable mapping was not in the conventional UV-layer list. Inspect shader Attribute nodes and named geometry attributes before regenerating mapping.

### 20.2 Specific lessons from actual reviews

- **Plaster:** broad color blotches looked dirty. Narrower color variation with subtle relief worked better. Review the entire wall.
- **Wood floors:** veneer images alone did not define the finish. Board identity, believable scale, edge detail, roughness, and reflection behavior mattered together.
- **Fabric:** full cushions, seams, and support were needed before textures could help. Avoid repeated procedural ridges that read as manufactured stripes.
- **Glass:** a dark interior behind a pane can come from a real exterior obstruction. Diagnose the whole light path before declaring the glass shader broken.
- **Metal/enamel/ceramic:** test curved and planar surfaces. One flat crop is insufficient for judging the material family.
- **Repetition:** variation should be purposeful. Repeated identical logs or regularly gridded media can remain visibly artificial even after contact is correct.
- **Dirt/wear:** small variation should reinforce use and construction; noise everywhere is not realism.

### 20.3 Preserve shader meaning

A shader digest should identify socket indices/identifiers, not only display names, because repeated names can lose information. Include images and their color spaces, world state, material bindings, and display settings.

Node-tree equality alone is incomplete. Material-level Cycles settings can change volume appearance while the node graph remains identical. A clean digest means only that the properties in that digest matched.

### 20.4 Portable textures

Deduplicate by exact file hashes, not names. Keep paths and licenses in an asset register. Reopen the portable copy on its intended path and test missing dependencies.

One tested save correction assigned destination-relative paths and saved with `relative_remap=False`, followed by reopening each scene. Do not apply that flag blindly to every project: verify the intended relative-path behavior for the actual source and destination.

Packed images may be available even when an old external path no longer exists. Conversely, packed textures do not imply that VDB caches, image sequences, linked libraries, fonts, sounds, or movies are all embedded. Audit each dependency class.

## 21. Furniture, assemblies, and contact

### 21.1 Treat assemblies as assemblies

A sofa can include frame, feet, seat, cushions, piping, and pillows. A pillow body and its separate welt curve must move and deform coherently.

We once corrected pillow bodies while leaving the piping behind. A later geometric midpoint-based seam adjustment passed contact measurements but produced a visible kink. Geometry checks and sewing plausibility were different requirements.

Keep explicit assembly IDs and enumerate constituent objects before a correction. Preserve the intended upper silhouette when correcting a lower bearing surface.

### 21.2 Support is a declared relationship

For each object, identify what it actually rests on:

- Chair foot → finished plank, not a lower substrate in a joint.
- Stove → hearth, not the room floor under it.
- Bowl → tray, then tray → floor.
- Upper log → a lower log or container base.
- Pillow → mattress/duvet, with the actual upper-floor elevation.
- Ceiling fixture → ceiling or mounting plate.

Names and bounding boxes help find candidates; they do not prove a support failure.

### 21.3 Sample contact carefully

Use evaluated world-space geometry. Record sample coordinates, upper/support object identities, query counts, missing-ray categories, and gaps in physical units.

For a hard foot, start with a small central flat sole patch. Wider rectangular samples can fall outside rounded corners or hit bevels. Preserve that distinction instead of reshaping a plausible rounded foot to satisfy the wrong sampling pattern.

Separate:

- No upper geometry at the sample.
- No sampled support geometry.
- A true positive gap.
- Intentional joint/recess depth.
- A rounded/beveled edge.
- Penetration.

A missing query is not automatically a missing floor. We obtained false failures from a support search that did not extend down to the actual substrate.

### 21.4 Correct without destroying the design

Bound a hard-foot adjustment and preserve its upper join and XY footprint where appropriate. A soft rug may need a different treatment; moving an entire table to satisfy a rug intersection is not a universal solution.

For stacked props, derive expected movement from the original support chain independently, not only from the builder's claimed translation.

Reopen the candidate and inspect a close view, context view, and if needed an underside/grazing diagnostic. A static diagnostic camera below furniture is not proof that a human-height tour camera can travel there.

### 21.5 Use a negative control

Deliberately raise a copied test foot in an isolated worker. The checker should report the gap. If it still passes, the checker is not measuring what you think.

Never save deliberate fault injection into the reviewed scene. Passing contact tests does not establish structural load capacity or finish the object's material realism.

## 22. Lighting and camera lessons

### 22.1 Diagnose geometry before adding light

We found an office whose room-to-window rays were clear, but the continued rays hit an exterior garage/roof obstruction. Removing glazing and changing transport settings did not solve the underlying darkness.

Test the complete path toward the environment, record exceptions for glass, and inspect actual openings. A clear segment ending just beyond a pane does not establish daylight access, and straight rays do not measure irradiance or full refracted transport.

### 22.2 Bracket practical lights

Use a few fixed-camera intensity variants at the same exposure. Select the lowest useful setting and compare neighboring control rooms. Inspect the fixture's actual attachment and its appearance, not just the average brightness of the image.

Our experiments found that making a room readable could expose appliance/support gaps previously hidden in darkness. Close the lighting defect and record the newly visible geometry defect separately.

An emitting mesh changes lighting even if the list of light objects and the world digest do not change. Clone shared materials before modifying emission, and protect original bindings.

### 22.3 Units and light calibration

Do not label artistic emission strength as watts, lumens, or lux. Geometry units alone do not establish a calibrated lighting setup.

An isolated native area-light fixture compared metre coordinates and foot-scaled coordinates at equal physical dimensions. It measured a scale-squared response difference, while a constant-world test was invariant. Compensation restored that fixture's reference response. This was a relative test, not a general electrical/photometric calibration.

The lesson is to run a tiny linear-EXR calibration experiment when physically interpreting light values. Do not multiply a visually balanced scene's lights by a guessed conversion factor.

### 22.4 Cameras need three different checks

1. **Origin clearance:** is the camera inside or too close to geometry?
2. **Target/sightline coverage:** does it see the intended body or room?
3. **Composition:** does the actual image communicate the space well?

A camera can pass the first test and face a blank shower wall. A bed target mask including the headboard can pass while the camera sees only its back. Declare the intended body, such as front bedding, rather than the whole assembly.

Update the view layer after setting camera transforms and inspect its evaluated pose. We encountered stale matrices that produced plausible-looking but wrong ray tests.

### 22.5 Practical camera recipes

Some successful room studies used approximately 22–24 mm lenses and camera heights around 1.45–1.52 m. These are project choices, not universal values or a reason to distort every room with a wide lens.

Keep verticals deliberate. Use shift/composition adjustments where appropriate, test projected geometry, and inspect actual views. A narrowly framed single bed does not prove a two-bed room's full coverage.

Model role and actual first-hit geometry matter more than naming conventions: a screen that recognized one door suffix missed doors carrying another suffix.

### 22.6 Render engines and hardware

Use a fast preview to inspect layout and a declared production engine for appearance decisions. Switching between Eevee and Cycles can change illumination and reflections; a preview-engine pass is not universal material acceptance.

On Apple hardware, explicitly detect/select an available Metal device if using it. On other hardware, use the installed Blender's supported backend. Record the backend actually applied, and make a CPU fallback explicit rather than silently relabeling a failed GPU render.

Keep hardware-specific settings scoped. A shadow-memory adjustment that helped one Eevee scene is not a universal recommended pool size.

## 23. Controlled experiments and regression tests

### 23.1 Keep the comparison controlled

Hold camera, resolution, samples, seed, engine, lights, exposure, world, and display transform fixed unless one is the stated variable.

Our early comparisons sometimes changed resolution and samples between revisions. They were useful qualitative evidence, not clean A/B measurements. Better evidence came from fixed recipes and fresh output directories.

### 23.2 Experiment template

```json
{
  "experiment_id": "E001",
  "hypothesis": "Reducing periodic cushion ridges improves the sofa close view",
  "parent_scene": "scenes/room-r003.blend",
  "parent_sha256": "MEASURE_ACTUAL_PARENT",
  "candidate_scene": "scenes/room-r004-candidate.blend",
  "change_scope": ["sofa seat geometry"],
  "protected": ["room layout", "camera", "lighting", "materials"],
  "proof_views": ["living-wide", "sofa-close", "sofa-grazing"],
  "technical_tests": ["unit scale unchanged", "protected geometry unchanged"],
  "visual_acceptance": "Ridges removed without flattening the cushion silhouette",
  "technical_verdict": null,
  "visual_verdict": null,
  "promotion_status": "PENDING"
}
```

### 23.3 What an image receipt should include

- Source scene, renderer script, and recipe hashes.
- Exact frame/timebase if animated.
- Named or explicit camera pose, evaluated pose, lens, sensor, clipping, and shift.
- Requested and actually applied engine, device, resolution, samples, seed, exposure, and display settings.
- Image filename, dimensions, size, and hash.
- Technical status and separate visual-review status.

Record applied settings, not merely requested settings. We caught a wrapper using an older renderer that ignored a new exposure field; all input hashes were valid, but the intended override was never applied.

### 23.4 Negative tests make verification useful

Use tiny isolated fixtures to inject faults such as:

- Wrong source hash or wrong reviewed parent.
- Missing image/volume file.
- Modified protected wall.
- Hidden light or excluded collection.
- Camera inside a wall or aimed away from the target.
- Raised support foot.
- Wrong VDB offset or native FPS.
- Requested exposure override not applied.

Verify the fault actually exists. An early camera fault test moved an animated camera before evaluating the frame; animation reset the move, so it had not tested the intended failure.

A valid PNG is not necessarily a good image. Decode/integrity checks, nearly-black-image screening, visual composition review, realism review, and release acceptance answer different questions.

### 23.5 Evidence reuse and scene lineage

Reuse a baseline only when source, renderer, recipe, view set, and image hashes match. Do not relabel an old image as a new proof.

After editing a Python helper, run a fresh process or explicitly reload the module. A cached import once left an older lineage rule active even though the file on disk had changed.

Keep one canonical reviewed-scene index. Derive duplicated `current_scene`/`next_action` fields from it, and assert the expected parent before a new build. Hash consistency alone does not say the chosen parent was visually accepted.

## 24. Walkthroughs and motion

### 24.1 Make a room-coverage plan

Write a route table with room IDs, entry/exit anchors, door state, floor elevation, camera height, target, transition, and required detail holds.

A full-house tour should cover the requested spaces. Do not silently replace it with a short highlights reel. Duration follows coverage and pacing unless the user supplies a duration cap.

### 24.2 Smooth motion comes from the route

- Plan continuous positional and rotational motion with eased transitions.
- Keep walking speed and turn speed coherent.
- Set required doors open in the intended scene revision.
- Check camera and near-plane clearance through doorways, stairs, and narrow passages.
- Check the forward view, not just the camera center.
- Avoid long blank-wall passages and sudden light/exposure jumps.
- Evaluate the actual frame timebase: `fps / fps_base`.

One early film used low rendered cadence plus synthesized frames and had reported turn glitches. A later native-30-fps approach improved the capture basis. Frame interpolation did not repair a poorly designed camera path.

Do not impose 30 fps on every scene without checking it: a later fire/room scene in the same program was saved at 24 fps. The source and intended delivery must agree.

### 24.3 Render a short native-frame test first

Test the hardest doorway, tight turn, glass passage, stair, dark-to-light transition, moving flame, and foliage. Inspect consecutive native frames and, where possible, actual playback at the intended cadence.

A sparse selection of pretty frames cannot establish smooth motion. A file decoding successfully cannot establish smooth camera motion either.

### 24.4 Save frames before encoding

Render numbered frames with resumable receipts. Missing frames, wrong frames, and changed settings should be detected before encoding. Preserve the image sequence as a recovery point when feasible.

An illustrative encoding command is:

```bash
ffmpeg -framerate 30 -i 'frame_%06d.png' -c:v libx264 -crf 18 -pix_fmt yuv420p tour.mp4
```

This is not a universal mastering recipe: substitute the actual frame rate and declare color management. Compare decoded video frames against the source frames before attributing brightness/flicker to the renderer.

### 24.5 Keep revisions honest

Label the source scene and review status of every movie. An earlier stylized tour remains an earlier stylized tour even when new realism stills look much better.

Browser chapters, route coverage, technical decode, motion appearance, and user acceptance belong in separate fields.

## 25. Volumes, fire, and advanced experiments

This is the frontier section: concrete lessons from scoped experiments, not a plug-and-play promise of physically correct combustion.

### 25.1 VDB file mapping

In a tested 200-file sequence numbered `0000`–`0199`, default offset behavior skipped the intended first file and later requested a nonexistent file. An offset of `-1` worked for that specific start-frame-1, duration-200, repeat setup.

Do not copy `-1` universally. Verify the evaluated loaded filename and file hash at the first, last, next/wrapped, and representative interior frames. Record source numbering, start frame, duration, offset, mode, and actual playback FPS.

### 25.2 Fit the visible field, not only the domain box

A volume's density domain can be much larger than its visible flame field. Measure the relevant grids across the sequence, retain source transforms, and distinguish object-space from world-space values.

We found a gas-flame field entirely inside its cavity that still appeared to float over the media. Sequence-wide bounds did not establish the visible emission's contact at a particular frame. Matched renders revealed the gap and a color fringe.

If no temperature grid exists, do not describe an artistic color ramp as measured blackbody temperature.

### 25.3 Judge appearance and spill together

An auxiliary point light placed inside a stove produced a bright disc through the glass. Actual paired renders caught it; removing that light improved the tested view while the volume/embers provided emission.

Low-resolution uniformly glowing coals exposed polygon faces in a close-up. Correct stacking/contact did not make their glow believable. A regular grid of stones remained artificial even with valid support; constrained variation helped the arrangement.

### 25.4 Material settings can matter outside the node tree

In a tested 5.2-era Cycles scene, interpolation and step-rate controls relevant to the volume were on the material's Cycles settings, not only on `volume.render`. Cubic interpolation plus a smaller step rate softened voxel stair steps in the tested close view. It did not create source detail that was absent.

Inspect the actual installed API and record these settings explicitly.

### 25.5 A renderer crash workaround with narrow scope

A tiny fixture reproduced a Cycles crash in a legacy RGB mix operation feeding a volume material, including on CPU. This did not support calling it Metal-only.

For the specific unclamped RGB MIX contract tested, replacing the operation with explicit `cool * (1 - factor) + warm * factor` rendered successfully on CPU and Metal and in the full room. Preserve the failing fixture and check the exact factor/alpha/blend contract before adapting it. This is not a blanket workaround for arbitrary mix modes, materials, or Blender versions.

### 25.6 Motion acceptance remains separate

Source simulation timestamps and preview FPS can differ. A small last-to-first grid difference is useful evidence, not proof of a seamless-looking loop. Inspect consecutive frames, including the wrap, and record which actual volume file each render loaded.

## 26. External assets and Geometry Nodes

### 26.1 Reuse good work where it helps

Evaluate whether an existing licensed asset will get you farther than building from primitives. Useful public starting points include Poly Haven, Kenney, and Sketchfab, subject to each asset's actual license and download terms.

Do not confuse “available to view” with “permitted to redistribute.” Record creator, source URL, license, attribution, downloaded file hash, unit scale, modifications, and dependency paths. A source-site-wide reputation is not an asset-specific license receipt.

### 26.2 Asset intake sequence

1. Confirm license and intended use.
2. Save the original source bytes and provenance.
3. Import into an isolated intake scene.
4. Measure individual object bounds and units; a catalog collection's bounds may describe an entire group.
5. Inspect UV layers **and** named attributes/material mapping.
6. Inspect geometry, texture resolution, materials, dependencies, and likely target-view cost.
7. Normalize only what the consumer actually needs.
8. Place in a candidate scene, render, reopen, and export/reimport if relevant.

An asset library does not automatically provide scale, coherent art direction, correct placement, or believable contact.

### 26.3 Geometry Nodes

Use node systems when a repeated family or distribution genuinely benefits: floorboards, trim families, scatter, foliage variation, facade components, or controlled layout variants.

Keep the input contract explicit, seed deterministic, and realized/evaluated export behavior checked. Save a small representative fixture before scaling to thousands of instances.

General Blender Python access can build Geometry Nodes graphs, but this guide does not claim a complete audited node library is bundled or that every sophisticated node workflow was proven in the source projects. Apply the same visual and export tests as with ordinary objects.

## 27. Exports, game assets, and browser delivery

### 27.1 Keep deliverable types separate

| Deliverable | Priorities |
| --- | --- |
| Editable `.blend` master | Full materials, dependencies, cameras, lighting, source organization, future changes |
| Offline still/film | Actual render appearance, declared scene revision, image quality and motion |
| Browser GLB | Supported materials, texture/geometry budgets, predictable scale, viewer behavior |
| Game asset | Consumer-specific topology, sockets, pivots, LODs, normals, textures/material contract |

Do not impose game-lane “no materials, flat shading, tiny triangle budget” rules on an architectural realism master. Those were specific export contracts for particular consumers.

### 27.2 Useful game-lane lessons

Our specialized game exports sometimes used POSITION+NORMAL, no UV/tangents/materials/cameras/lights/animation, and flat normals, because the consumer supplied the intended look. These are **not** universal glTF defaults.

Triangle limits are ceilings, not artistic targets. Spend geometry on silhouette, readable shape, joins, and expected camera distance. Conversely, an absurdly small asset that reads as a few boxes can be a quality failure despite being below budget.

A low-detail tier should provide a meaningful reduction or a declared consumer-supported alias. Delivering identical “hi” and “lo” files does not establish a useful LOD strategy.

Quantized attributes can reduce bytes, but only use extensions the target loader actually supports. Do not assume compression/quantization preserves every consumer's behavior. Audit exported bytes, reimport, and inspect the real target.

### 27.3 Socket and placement contracts

Before building a host-dependent asset, verify required socket names and transforms in the actual reference file. Keep protected host digests. A socket-only repair should not silently remesh or re-shade the host.

Ship placement/assembly data if needed: transforms, parent/instance identity, pivot, forward/up axes, units, socket usage, collisions, animation expectations, materials, and example application code.

Hundreds of correct GLBs can still fail intake if nobody knows where or how to place them. “Delivered assets” and “working level” are different outcomes.

### 27.4 Browser appearance is a new test

Native Blender shaders can use nodes and effects a glTF viewer does not reproduce. Bake or simplify deliberately where needed. Preserve the native master.

Test actual geometry, scale, textures, transparency, metallic/roughness behavior, environment/exposure, supported extensions, and default camera behavior in the intended viewer. Then test a representative phone if mobile is part of the brief.

Opening a GLB without an exception is not a material-fidelity pass. A nice Cycles PNG is not evidence that the browser GLB looks the same.

### 27.5 A small independent GLB container audit

Save this as `inspect_glb.py` and run it with a file path. It uses only standard Python and reads the exported file. It checks the container and summarizes metadata; it does **not** decode every vertex, certify bounds, count rendered instances, or validate visual quality.

```python
import hashlib
import json
import struct
import sys
from pathlib import Path

path = Path(sys.argv[1])
raw = path.read_bytes()
if len(raw) < 12:
    raise SystemExit("Too short for a GLB header")
magic, version, declared_length = struct.unpack_from("<4sII", raw, 0)
if magic != b"glTF" or version != 2 or declared_length != len(raw):
    raise SystemExit("Invalid GLB header/version/length")
offset = 12
chunks = []
doc = None
while offset < len(raw):
    if offset + 8 > len(raw):
        raise SystemExit("Truncated chunk header")
    length, kind = struct.unpack_from("<II", raw, offset)
    offset += 8
    if length % 4 or offset + length > len(raw):
        raise SystemExit("Misaligned or truncated chunk")
    payload = raw[offset:offset + length]
    chunks.append({"type": kind, "bytes": length})
    if kind == 0x4E4F534A:
        if doc is not None or len(chunks) != 1:
            raise SystemExit("Unexpected JSON chunk placement/count")
        doc = json.loads(payload.decode("utf-8").rstrip(" \x00"))
    offset += length
if doc is None:
    raise SystemExit("Missing JSON chunk")

accessors = doc.get("accessors", [])
triangles = 0
nontriangle_primitives = 0
for mesh in doc.get("meshes", []):
    for primitive in mesh.get("primitives", []):
        if primitive.get("mode", 4) != 4:
            nontriangle_primitives += 1
            continue
        accessor_id = primitive.get("indices")
        if accessor_id is None:
            accessor_id = primitive.get("attributes", {}).get("POSITION")
        if not isinstance(accessor_id, int) or not 0 <= accessor_id < len(accessors):
            raise SystemExit("Missing/invalid primitive accessor")
        count = accessors[accessor_id]["count"]
        if count % 3:
            raise SystemExit("Triangle-list count is not divisible by 3")
        triangles += count // 3

external = []
for category in ("buffers", "images"):
    for item in doc.get(category, []):
        uri = item.get("uri")
        if uri and not uri.startswith("data:"):
            external.append({"category": category, "uri": uri})
print(json.dumps({
    "file": path.name,
    "bytes": len(raw),
    "sha256": hashlib.sha256(raw).hexdigest(),
    "chunks": chunks,
    "nodes": len(doc.get("nodes", [])),
    "meshes": len(doc.get("meshes", [])),
    "triangle_list_resource_triangles": triangles,
    "other_primitive_modes_not_counted": nontriangle_primitives,
    "materials": len(doc.get("materials", [])),
    "images": len(doc.get("images", [])),
    "extensions_used": doc.get("extensionsUsed", []),
    "external_dependencies": external,
    "scope": "container and metadata only; reimport and view separately",
}, indent=2))
```

For instancing, strips/fans, compressed data, skinning, actual world bounds, or flat-normal invariants, add a real decoded geometry audit or consumer-side test. A resource triangle count is not an instance-expanded scene count.

## 28. Portable projects and dependable handoffs

### 28.1 A minimal useful delivery package

Include:

- `START-HERE.md`: what this is and how to open it.
- Editable scene(s) with exact revision/status.
- Needed dependencies and asset/license register.
- Export(s) for the intended consumer.
- Actual reviewed proof images and, where applicable, film/chapters.
- Scripts/recipes needed to reproduce the work.
- Manifest with filenames, byte sizes, SHA-256, units, and appropriate counts.
- Known flaws, unverified checks, integration instructions, and exact next action.

Use relative paths inside a portable bundle. Keep machine-specific connection records and credentials out of it.

### 28.2 Delivery is a separate verification

Check that the destination has the actual bytes and matching hashes. A file in the Blender host's Downloads folder is not automatically a file in the chat sandbox, staging machine, Git repository, or website build.

Do not say “uploaded” because a local export exists. Do not say “live” because a ZIP was handed to the integrator.

### 28.3 A fresh-session handoff

```text
ASSIGNMENT
What the user authorized; complete output roster; latest amendments.

ENVIRONMENT
Machine roles; versions; executable/connection details without secrets.

LAST VERIFIED ACCESS
Timestamp, actual probe result or error; what has not been retested.

REVIEWED PARENT
Exact file and measured hash; accepted scope; protected objects/contracts.

PENDING WORK
Candidate files, active job identity, receipts/logs, uncertain write outcomes.

DELIVERED
Destination, filenames, sizes/hashes, export/visual/integration status separately.

READY NEXT
One precise next operation; independent work that can proceed.

HOLDS AND FLAWS
Missing references, failed checks, unreviewed rooms/frames/exports.
```

A fresh assistant should inspect this state and continue, not rebuild everything or revive an old queue. If the assignment is present and authorized, connection verification and real work should follow promptly.

## 29. Copyable operating prompts

### Setup coach

> Guide me through the Blender Lab MCP route using current official docs and this field guide. First inspect whether I already have a compatible setup. Identify my client surface, operating system, and the machine hosting Blender. Give me two or three steps at a time. Keep keys local. Prove the add-on, bridge, tunnel if used, tool discovery, and a fresh read-only Blender execution separately. Do not call setup complete from a screenshot or configuration doctor alone. Then help me run the isolated first-project exercise.

### First production assignment

> Build one furnished study-room visualization in a new project. Use real-world units, named objects, reproducible scripts, and saved revisions. Start with room geometry/openings/furniture scale, then improve form, materials, light, and presentation. Save an editable `.blend`, GLB, three actual rendered views, a manifest, and a concise flaws list. Perform at least three meaningful review-and-correct rounds. Inspect every proof image you claim to review. Continue through the authorized work without asking permission between ordinary steps.

### Whole-house assignment

> Convert my attached room program into a measurable layout contract. Propose floor count and area, explain the tradeoffs, and build a named whole-house scene in real-world units. Reconcile net/gross/garage/exterior areas explicitly. Review floor plans, four corners, entry sightlines, openings, and furniture clearance before styling. Preserve unknowns. Deliver editable scenes, exports, schedules, actual renders, and honest PASS/FAIL/NOT MEASURED checks. Make no structure, code, cost, or buildability claims.

### Realism pass

> Refine the existing scene without changing its protected layout. First inspect the actual file, units, camera, materials, lighting, and reviewed parent. Rank the largest visible realism defects. Choose one bounded experiment, declare acceptance, render matched before/after wide and close views, measure applicable contracts, inspect actual images, and accept or reject it. Save failures. Reuse a successful improvement on a second relevant scene before calling it transferable. Keep technical and visual verdicts separate.

### Camera-tour assignment

> Produce a full POV tour of the requested spaces using the actual selected scene revision. Build a room-coverage route and declared native timebase. Open required doors, check clearance and forward framing, render short native-frame tests of difficult transitions, inspect motion, and then render a resumable frame sequence. Deliver the video, chapters, source route/capture scripts, scene hash, technical checks, and remaining visual flaws. Do not substitute a highlights reel or label an older movie as a new realism revision.

### Connection recovery

> Diagnose the existing connection without reinstalling or disturbing the current scene. Map the add-on, bridge, tunnel client, and custom MCP connection. Identify each relevant process by executable/arguments and actual port ownership. Read narrow non-secret diagnostics. Distinguish idle timeout, busy operation, crash, missing local file, and upstream transport error. Preserve active job evidence before restarting. Make one read-only execution test after a real recovery step and report the exact observed result.

### Continue an interrupted project

> Read START-HERE, NEXT, state, reviewed-scene index, and the latest user amendments. Verify file hashes and inspect active-job receipts/liveness before starting anything. Resolve pending versus reviewed branches. Preserve holds and do not rebuild accepted work. Continue the next ready authorized item, saving state and reporting concrete progress as you go.

### Improve the workflow itself

> Identify one recurring defect and turn the fix into a small reusable tool or documented recipe. Define its input/output contract, test a positive case and a deliberately failing case, record limits, and apply it to a second representative project. Keep the tool version separate from scene versions. Do not claim broad automation from one successful example.

## 30. Readiness and release checklists

### Connection readiness

- [ ] Intended machine and client surface identified.
- [ ] Add-on and matching MCP bridge identified; versions recorded.
- [ ] Actual listener/port ownership confirmed.
- [ ] Runtime environment/paths verified.
- [ ] Tunnel target/profile/health checked if this route uses a tunnel.
- [ ] Workspace association/authentication checked where required.
- [ ] Actual tools discovered in this conversation.
- [ ] Fresh read-only code returned from Blender.
- [ ] Test save/export/render verified in an isolated project.
- [ ] Restart/credential/log record saved without secrets.

### Per-experiment readiness

- [ ] Parent identity and source hash verified.
- [ ] Defect/hypothesis and acceptance criteria written.
- [ ] Scope/protected objects specified.
- [ ] Units and evaluated geometry understood.
- [ ] Fixed camera and render recipe recorded.
- [ ] Candidate saved separately.
- [ ] Applicable technical checks passed, with checker limits stated.
- [ ] Actual wide/close/context images inspected.
- [ ] Negative test used where a new checker could otherwise give false confidence.
- [ ] Verdict and next action recorded; rejected evidence retained.

### Delivery readiness

- [ ] Native scene reopened; required dependencies available.
- [ ] Export inspected from disk and in the intended consumer.
- [ ] Units, names, sizes, counts, and hashes measured as applicable.
- [ ] Named views/spaces covered; unreviewed parts marked honestly.
- [ ] Motion reviewed if delivering motion.
- [ ] Master, browser, and film revisions labeled independently.
- [ ] Licenses/provenance included for redistributed assets.
- [ ] Destination bytes verified.
- [ ] Integration instructions are sufficient for another person to use the work.
- [ ] Known flaws and exact next action included.

## 31. What this guide does not promise

- A Markdown attachment cannot grant tool access, provision an account, override a permission refusal, or keep a computer online.
- Our 250-ms timer profile is an observed configuration, not a universal cure or a scientifically optimized setting.
- The prepared macOS Blender LaunchAgent repair was not proven installed in the referenced recovery incident.
- No current connection is claimed merely because a historical test succeeded.
- A valid file, complete manifest, or clean audit does not prove visual quality.
- Scoped still/frame success does not establish a final realistic whole-house film, all-room completion, or browser/mobile acceptance.
- General Python access does not mean a complete rigging, simulation, Geometry Nodes, or asset-library pipeline is already installed.
- This one file contains operating knowledge and small example recipes, not every historical production script, private source file, model, or paid asset.
- Render time and language-model usage are separate resources. Do not estimate remaining account quota from a render's duration or pretend the assistant can read a usage meter it has not been given.
- Strong visualization does not establish engineering correctness or construction readiness.

The practical advantage is starting with a process that keeps producing inspectable, recoverable improvements. There is no requirement to repeat the same months of setup confusion, lost state, false passes, or export-versus-integration mistakes.

## 32. Public references and provenance

### Public setup references

The product pages below were consulted during preparation. Some source/manual pages were blocked by the retrieval service; they are included as the authoritative places for the receiving assistant/user to check, not falsely described as fully retrieved here. Software and UI labels change: inspect live docs and installed help before applying version-specific installation commands.

- **[S1] Blender Lab MCP:** <https://www.blender.org/lab/mcp-server/>; source <https://projects.blender.org/lab/blender_mcp>; setup <https://projects.blender.org/lab/blender_mcp/wiki/Setup>. Official Lab-page content was retrieved through search; direct source/setup retrieval was unavailable.
- **[S2] Community MCP for Blender, a different project:** <https://github.com/ahujasid/mcp-for-blender>. Consulted to distinguish implementations, not to claim its current package matches our historical Lab setup.
- **[S3] OpenAI MCP configuration:** <https://learn.chatgpt.com/docs/extend/mcp>. Local Codex configuration, hosted-chat distinction, registration and timeout controls.
- **[S4] OpenAI Secure MCP Tunnel:** <https://developers.openai.com/api/docs/guides/secure-mcp-tunnels>. Private connectivity, permissions/workspace association, supported surfaces, and distribution boundary.
- **[S5] OpenAI tunnel-client end-user guide:** <https://github.com/openai/tunnel-client/blob/master/docs/end-user-guide.md>. Credential roles, readiness/discovery distinction, and operator checks.
- **[S6] OpenAI tunnel-client onboarding:** <https://github.com/openai/tunnel-client/blob/master/docs/onboarding.md>. CLI/profile examples and current managed-runtime lifecycle. Check the installed version's help.
- **[S7] Add a custom MCP server in ChatGPT:** <https://developers.openai.com/api/docs/guides/custom-mcp-server>. Current connection UI, authentication, installation, and tool management.
- **[S8] Apple launchd background jobs:** <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>. User-agent lifecycle and job design. For modern command details consult the local `man launchctl`.
- **[S9] OpenSSH configuration:** <https://man.openbsd.org/ssh_config.5>; command reference <https://man.openbsd.org/ssh.1>. Effective configuration and forwarding-failure semantics.

### Additional authoritative starting points

- Blender command-line reference: <https://docs.blender.org/manual/en/latest/advanced/command_line/arguments.html>
- Blender glTF exporter: <https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html>
- Blender Python API: <https://docs.blender.org/api/current/>
- Blender packed data: <https://docs.blender.org/manual/en/latest/files/blend/packed_data.html>
- glTF specification: <https://registry.khronos.org/glTF/>
- Poly Haven: <https://polyhaven.com/>
- Kenney: <https://kenney.nl/>
- Sketchfab license guidance: <https://sketchfab.com/licenses>

Blender's direct manual/API pages were not retrievable during this guide's preparation. Prefer the installed Lab server's bundled documentation-search tools where available, or open the official manual locally. The examples here are original illustrative recipes and are not claimed to be an exhaustive API reference.

### Where the experience-based advice came from

The exact tuning table and recovery distinctions came from recorded local settings, actual connection errors, and a successful historical tunneled execution. Production lessons came from saved project/handoff records and the architectural-realism workflow's evidence-backed lessons, including rejected experiments. The frontier capability summary also uses retrieved historical project reports; those reports were not all independently rerun for this document.

The public text omits personal names, household information, private hostnames, account identifiers, real local usernames, live tunnel IDs, secret values, private repository links, and proprietary project files. Fictional room names and `org.example` labels are examples.

Receiving assistant: start at Section 5, test what is actually available, and then help the user make something worth keeping.
