# Bellona Blender executable smoke — Phase 5.3 (2026-09-06)

Pinned real-Blender smoke for `EVE_SOTA_GAP_CLOSURE_TODOS_2026-09-01` task 5.3:
probe the actual executable on this host, then handshake → health → safe
`execute_python` → save/readback → export, all driven through the **shipped**
`@bellona/blender-agent` stdin RPC bridge **outside the agent/LLM planning
loop**. A different host's record is not availability evidence; this record is
for the host that produced it.

## Host executable (probed)

| field                                        | value                                                                                                                          |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| executable path                              | `/usr/bin/blender`                                                                                                             |
| version banner (`blender --version`)         | `Blender 4.0.2`                                                                                                                |
| session `blenderVersion` (from `health`)     | `4.0.2`                                                                                                                        |
| architecture                                 | `x86_64`                                                                                                                       |
| OS                                           | Linux                                                                                                                          |
| Blender Python                               | 3.12.3                                                                                                                         |
| mode                                         | headless (`background: true`)                                                                                                  |
| headless recipe                              | `blender --background --factory-startup --python <bootstrap>`                                                                  |
| add-on state                                 | `bellona_addon` importable (has `bl_info`); NOT enabled as a live WS server in this smoke — the stdin transport needs no addon |
| enabled built-in exporters (factory-startup) | `io_scene_gltf2`, `io_scene_fbx`, `io_mesh_stl`, `io_scene_x3d`, `io_curve_svg`, `io_anim_bvh`, `io_mesh_uv_layout`            |

Probe order mirrors the shipped `blenderExecutableCandidates()` in
`apps/bellona/remote-host/src/blender-adapter.ts`:
`BELLONA_REMOTE_HOST_BLENDER_PATH` → `BLENDER_EXECUTABLE` →
`/Applications/Blender.app/Contents/MacOS/Blender` (macOS) → `blender` on PATH.

## Smoke sequence (shipped stdin bridge, real Blender 4.0.2)

| step                                                | result                                                                                                       |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| handshake + health                                  | protocol `1.0`, `executionMode: headless`, `sessionId: stdin:4.0.2`, supports python/file/operator execution |
| safe `execute_python`                               | created `SmokeCube`; object count → 4                                                                        |
| save `.blend` + readback                            | reported 853,784 bytes == on-disk 853,784; magic `BLENDER`                                                   |
| export `.glb` (`export_scene.gltf`, GLB) + readback | reported 3,448 == on-disk 3,448; magic `glTF` (binary glTF)                                                  |
| export `.obj` (`wm.obj_export`)                     | 1,866 bytes written and read back                                                                            |

## Observed refusal when the executable is absent

Driving the same shipped bridge with
`blenderExecutable: /nonexistent/blender-does-not-exist` fails loud — no
fabricated success:

```
refusal.result = "refused"
refusal.error  = "spawn /nonexistent/blender-does-not-exist ENOENT (request <uuid>)"
```

The spawn error is delivered on the child-process `error` event and
`cleanupPending` rejects the in-flight `handshake` request with it.

## Reproduce

Gated integration spec (runs only when a Blender executable is configured, skips
otherwise):
`libs/bellona/blender-agent/src/blender-executable-smoke.integration.spec.ts`.

```
BELLONA_AUDIT_BLENDER_PATH=/usr/bin/blender \
  npx vitest run libs/bellona/blender-agent/src/blender-executable-smoke.integration.spec.ts
```

## Limits

One host, one Blender (4.0.2, x86_64 Linux), headless only. This is first-light
smoke evidence for the stdin transport — it proves the executable is reachable
and drivable to save/export on this host. It does NOT prove the WebSocket-addon
path against real Blender, macOS/arm64, breadth, or governed leased execution
(tasks 5.6–5.8). The `.blend`/`.glb`/`.obj` byte counts are host- and
version-specific and will differ elsewhere.

## Task 5.20 compressed-save repair checkpoint (2026-09-16)

The original smoke's `.blend` check was not compression-safe: it required the
first seven bytes to be ASCII `BLENDER`. The repaired executable probe now saves
the same scene both uncompressed and compressed, resets Blender, reopens each
file in Blender itself, and compares the named object, transform, dimensions,
material, frame range, FPS, and unit settings. It then exports each reopened
scene to GLB and OBJ, matches reported bytes to disk, and decodes both formats
to verify the object, material, and mesh content.

The source candidate is `49eaec056c4312fa75f44209d71a3cdd94e6ac7d`. The
source-bound local receipt is
`docs/audits/eve-sota-blender-save-export/2026-09-16.linux.probe.json`. On Linux
x86_64 Blender 4.0.2, the uncompressed file began with `BLENDER`; the compressed
file began with a different byte sequence; both reopened with identical scene
semantics. Corrupt, truncated, wrong-format, and missing files were refused, and
the Blender process and temporary root were cleaned up.

This is an implementation and local-regression checkpoint, not Task 5.20
closure. The specifically required macOS Blender 5.0.1 reproduction/retest and
independent DCC QA are still missing.
