TextureCraft is the ArtCraft team's open-source, pure-Rust 3D texture-painting app — a clean-room reimplementation of the Adobe Substance 3D Painter texturing workflow (`ROADMAP.md`): paint PBR materials straight onto 3D models in layers with masks and generators, bake mesh maps, drive smart materials, and export texture sets for any engine (glTF, Unreal, Unity, Blender, Godot, OpenPBR). Trigger this skill whenever the user mentions TextureCraft — or a `.tcraft`, `.tcmat` file — asks what it ...
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Texturecraft?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/midasmatterhorn-texturecraft)
---
name: texturecraft
description: >
TextureCraft is the ArtCraft team's open-source, pure-Rust 3D texture-painting app — a clean-room
reimplementation of the Adobe Substance 3D Painter texturing workflow (`ROADMAP.md`): paint PBR
materials straight onto 3D models in layers with masks and generators, bake mesh maps, drive smart
materials, and export texture sets for any engine (glTF, Unreal, Unity, Blender, Godot, OpenPBR).
Trigger this skill whenever the user mentions TextureCraft — or a `.tcraft`, `.tcmat` file — asks
what it can or cannot do, which mesh formats it opens, how to bake ambient occlusion / curvature /
normal maps, how to script, automate, batch or headlessly drive it (texturecraft-cli, MCP, the
JSON control channel), wants textures painted or exported for a game engine, needs baked maps
verified, or troubleshoots it. Also when the job is described without naming the app — "paint
rust onto this model", "bake an AO map for this mesh", "export a glTF PBR texture set", "make a
smart material from these layers", "reimport the mesh without losing my paint". Do not guess its
capabilities from Substance Painter knowledge — read this first.
---
**Audit basis.** Refreshed 2026-11-07 against tag **v0.4.0**, commit **`16e9d21516434a63ac457f49ffd09005f649c02f`** — `git rev-parse HEAD` in the pinned checkout confirms. The installed bundle (see Sources & evidence) prints `TextureCraft 0.4.0 (16e9d21 2026-11-06)`. This skill previously described v0.3.0 (`8547514`).
**v0.3.0 → v0.4.0 delta.** `git diff --stat v0.3.0..HEAD` shows **216 files changed, +14,040/−5,788** (the GitHub compare lists 86 commits). The engine registry moves 162 → **172** ids: `camera.bookmark1`…`camera.bookmark9` (Ctrl+1…Ctrl+9), `bake.tile.set`, `bake.cage.paint` and `bake.cage.reset` are new; `bake.autoRebake` and `bake.autoRebake.run` are gone, so nothing re-bakes itself — call `bake.run` after changing bake settings, the mesh, high polys or the skew/cage paint. Bake settings were rebuilt into a nested per-map `BakeSettings` model (`crates/core/src/bake/`, `crates/engine/src/bake/`): `bake.settings` takes `model`, `patch` and `preserveEnabled` and targets `ts` (the active set by default, an index, an array, `"all"` or `"selected"`); `bake.run` accepts the same parameters and returns `{baked, tracer}`, and each texture set owns its settings plus per-tile bake flags. The baked-map set shrinks 12 → **10**: `ColorMap` and `WorldDirection` are removed (`crates/core/src/bake/baked_map.rs`). Six smart masks (snow from top, moss from ground, rust in cavities, edge scratches, dirt streaks, occluded areas) take the shelf to 24; three new Views entries (Common Settings, Mesh Map Bakers, Mesh Map Settings) take menu parity to **58/58**. Old `.tcraft` files whose bake settings or baked maps no longer parse are recovered field by field with defaults and a log warning (`crates/formats/src/project_file/recovery.rs`); `file.reimportMesh` now marks the project modified (`crates/engine/src/file_cmds.rs`); masks and content evaluate **every** neighbourhood filter in stack order instead of only the last (`crates/compose/src/eval.rs`); exports sample baked maps through the working tile layout (`crates/formats/src/export/mod.rs`). UI-side: mode buttons and view-layout/projection dropdowns in the top bar, a draggable 3D/2D divider, scrolling tool controls, smart-mask and shelf hover previews, and dropping a shelf material on the texture set under the cursor (`crates/ui-egui/src/{toolbar,viewport,shelf}.rs`).
**v0.2.0 → v0.3.0 delta (historical).** 168 files changed; the engine command extract grew 135 → **162** ids (+27: `bake.autoRebake*`, `bake.skew.*`, `brush.preset.*`, `effect.copy/cut/duplicate/histogram/move`, …), none removed then. Menu parity 55/55. The control channel hardened: owner-only token file, 10 s auth deadline, one attempt per connection, 16-connection cap, 600 s idle.
**Verify against your installed version** — the ArtCraft Launcher auto-updates, so a newer build may differ; every count quoted here was re-measured at this tag with the method recorded in `evidence.md`.
# TextureCraft
## Overview
TextureCraft is an open-source (MIT OR Apache-2.0), pure-Rust 3D texture-painting application — the ArtCraft suite's clean-room answer to Adobe Substance 3D Painter (the repo aims for 1:1 parity with that texturing workflow, with better performance, a smaller footprint and every feature drivable by agents — `ROADMAP.md`). Its own weighted estimate is **≈94%** done across 11 areas (`ROADMAP.md` "Honest parity estimate (2026-11-04)"; baking was lowered to 90% pending reference-asset validation). It runs natively on macOS (universal, signed, notarized), Windows, Linux, FreeBSD and in the browser via WebAssembly/WebGPU-or-WebGL2 (`README.md`, `ROADMAP.md` Platforms). Source: GitHub `storytold/texturecraft`. The workspace is 11 crates (`automation`, `bake`, `compose`, `core`, `engine`, `formats`, `geom`, `paint`, `render`, `texel`, `ui-egui`) plus three apps (`apps/texturecraft`, `apps/texturecraft-cli`, `apps/texturecraft-web`).
The one governing idea: **everything is an engine command with a stable id** (`layer.add.fill`, `bake.run`, `edit.undo`, … — `docs/control-protocol.md`), and the UI, `texturecraft-cli`, the JSON control channel and the MCP server all run the same registry (**172 unique command ids** at v0.4.0; the generated menu doc counts **58 of 58 menu items live**, `docs/parity.md`, regenerated by `TC_UPDATE_PARITY=1 cargo test -p texturecraft-ui-egui parity`). Programmatic calls never open dialogs; `session.inspect` reports every layer, mask, effect, fill source, per-tile `bakeEnabled` flag and painted tile count so an agent can verify work without screenshots.
## Capabilities (verified)
**Count status at v0.4.0** (every set re-measured at both tags; method in `evidence.md`). Changed: engine ids **172** (162), menu items **58/58** (55/55), smart masks **24** (18), baked maps **10** (12; `ColorMap`/`WorldDirection` removed), project `format_version` **2** (1). Unchanged at v0.4.0: CLI 10 subcommands, MCP 22 tools, serve 17 methods, control 13 methods, 31 filters, 21 channels, 32 blend modes, 72 procedural + 21 smart materials, 23 procedurals, 6 environments, 67 brush alphas, 18 brush presets, 10 export presets, 8 texture formats, 12 mesh import / 5 mesh export formats, 9 built-in meshes, 10 UI languages.
### One command registry, four surfaces
- **Engine registry: 172 unique command ids** (every `cmd!`/`query!` registration with a string id across `crates/engine/src`, tests excluded; `texturecraft-cli commands [--filter]` prints the live set for the build in front of you). All **58/58** target menu items are wired (`docs/parity.md`).
- **CLI** `texturecraft-cli` — **10 subcommands** (`apps/texturecraft-cli/src/main.rs` `SUBCOMMANDS`): `info`, `commands [--filter]`, `run <input> <command-id> [params-json] [-o out.tcraft]`, `batch <script.json>`, `bake <input> [-o out] [--resolution px] [--maps id,id]`, `export <input> <dir> [--preset --format --padding --size]`, `render <input> <out.png> [--size WxH --view 3d|2d --camera name --yaw --pitch --samples --bounces] [--cpu]`, `new <input> <out.tcraft> [--resolution] [--sample]`, `mcp [--bridge --token --read-root --write-root --automation-read-root --automation-write-root]`, `serve [--port --read-root --write-root --automation-read-root --automation-write-root]`; plus `--version`/`--help` (hand-rolled parser, not clap).
- **Control channel** — `texturecraft --control <port>`, loopback TCP, one JSON object per line, 8 MiB request cap. **Token-authenticated**: the first request on every connection must be `{"id":0,"method":"auth","params":{"token":"…"}}`; the token is `TEXTURECRAFT_CONTROL_TOKEN` (16–1024 printable ASCII characters) or a fresh random token the app writes to `<temp>/texturecraft-control-<port>.token` (owner-only) and prints to stderr. One auth attempt, 10 s deadline, 16 connections, 600 s idle (`docs/control-protocol.md`, `crates/automation/src/security.rs`). **13 methods**: `ping`, `command.run`, `command.list`, `session.inspect`, `ui.inspect`, `ui.menu.list`, `ui.menu.invoke`, `ui.set` (layout/dock/prefs/keymap — `prefs` covers `language`, `brightness` (4 levels), `accent` (7 colours), `system_title_bar`), `viewport.screenshot {view: 3d|2d}`, `ui.screenshot`, `ui.resize` (640–7680 points), `debug.paintTest {from,to,frames}` (a real per-frame scripted stroke), `debug.frames` (frame timing breakdown). Scripted runs can set `TEXTURECRAFT_NO_PREFS` to start from defaults in a throwaway state.
- **MCP server** `texturecraft-cli mcp` — **22 tools** (`crates/automation/src/server.rs` `#[tool]` entries): `session_inspect`, `command_list`, `command_run`, `command_batch` (stops at first error, ≤256 steps), `project_new` (9 built-in meshes: material_ball, sphere, cube, rounded_cube, cylinder, torus, plane, crate, udim_plane; templates), `project_open`, `project_save`, `mesh_import` (glTF/GLB, FBX binary+ASCII, COLLADA, USD usda/usdc/usdz, Alembic, OBJ, PLY, STL), `mesh_reimport` (**`preserveStrokes: true` re-projects paint where UVs changed**; it is off by default), `mesh_export` (obj, glb, usda, usdc, usdz), `bake_mesh_add` (high-poly + cage), `resource_import`, `material_save`/`material_load` (`.tcmat` smart materials; load also takes MaterialCraft `.mcmat`/`.mcraft`), `textures_export` (preset-driven), `bake`, `paint_stroke` (points in viewport pixels, sharing one space with `viewport_render`), `viewport_render` (headless PNG ≤2048 px, optional path-traced `samples` 1–4096 on GPU or CPU), bridge-only `viewport_screenshot`/`ui_screenshot`/`ui_inspect`/`ui_menu_invoke`. **Path confinement**: headless mode touches files only below `--read-root`/`--write-root` (forward-slash relative paths; `..`, absolute paths, drive letters, device names, symlink/junction escapes refused); through `command_run`, every `file.*` command is refused except `file.close`, `file.removeUnused` and `file.reimportMesh` without a `path`, and so is any command given a `path`/`dir`, in both modes — use the workspace tools. Replies capped at 8 MiB JSON / 5 MiB PNG / 256 batch steps.
- **Headless JSON-lines server** `texturecraft-cli serve` — **17 methods** (`crates/automation/src/rpc.rs` `METHODS`): `ping`, `methods`, `session.inspect`, `command.list`, `command.run`, `command.batch`, `project.open`, `project.save`, `mesh.import`, `mesh.reimport`, `mesh.export`, `bake.mesh.add`, `resource.import`, `material.save`, `material.load`, `textures.export`, `render`.
### Painting, layers and materials
- **Paint on the 3D mesh or the 2D UV view**: brush (size/flow/stroke-opacity/spacing/hardness/angle/follow-path, jitters, tangent or camera alignment, angle fade, backface culling, lazy mouse, X/Y/Z mirror + radial symmetry, pen pressure), eraser, **projection with image stencils** (hold S), polygon fill (triangle/UV-island/mesh), **path and lasso fill**, **particle brush**, smudge, clone (V + click sets source), colour picker, Shift+click straight lines, **quick mask** (Y), **geometry decals** and **text** (`docs/features.md` §Paint in 3D). **67 procedural brush alphas** (31 generated shapes + 36 stencil glyphs, `crates/paint/src/alphas.rs`) plus any imported image; **18 brush presets** in three groups, with user presets saved as `.tcbrush` (`crates/paint/src/presets.rs`, `crates/engine/src/brush_preset_cmds.rs`).
- **Layer stack**: paint, fill, folder and **instance** layers; **32 blend modes per channel** with per-channel opacity; white/black/painted masks, **mask with height**, smart masks, generators; **31 library filters** (`crates/engine/src/filter_lib.rs` `FILTERS`: adjustments, blur/sharpen/high-pass, bevel, glow, edge detect, warps, mosaic, baked lighting, four material finishes); **geometry masks** (a layer visible only on chosen mesh parts); anchor points; copy/paste across texture sets, flatten, save layers/folders as `.tcmat` smart materials. All **24 smart masks** are procedural and expose their preset id: `mask.smart.list` to enumerate, `mask.smart.apply {name, layer?}` to replace a layer's mask; the six new ones are `snow_top`, `moss_ground`, `rust_cavities`, `edge_scratches`, `dirt_streaks`, `occluded` (`crates/engine/src/smart_masks.rs`).
- **Bundled library**: **72 procedural materials and 21 smart materials** (93 shelf entries), **24 smart masks**, **23 procedurals**, **6 procedural environments** plus imported HDR/EXR panoramas (`crates/engine/src/materials.rs` `MATERIALS`, `crates/core/src/layer.rs` `ProceduralKind`, `crates/core/src/project.rs` `ENVIRONMENTS`). A shelf material can be dropped on the texture set under the cursor, and hover shows a large preview (`crates/ui-egui/src/shelf.rs`). **MaterialCraft materials**: `material.load` accepts a `.mcmat` portable material or `.mcraft` package as a fill layer, re-cooked through MaterialCraft's command line with the set's baked mesh maps (`docs/control-protocol.md` §MaterialCraft materials, `crates/engine/src/link_cmds.rs`).
- **Channels**: 21 `Channel` variants per texture set — base color, metallic, roughness, height, normal, emissive, opacity, AO, specular level, specular, glossiness, clear coat weight/roughness/normal, sheen weight/colour/roughness, anisotropy level/angle, subsurface scattering weight, transmission (`crates/core/src/channel.rs` `pub enum Channel`). Texture sets 128–8192 px, **UDIM/UV tiles** with per-tile working resolution and `$udim` export.
- **Viewport**: PBR with IBL, ACES/linear tone mapping, depth of field, displacement, 4× MSAA + idle temporal accumulation, channel/mesh-map/mask/UV-checker views, saved cameras (also imported from glTF/FBX/COLLADA/USD) with **Ctrl+1…9 bookmarks** (`camera.bookmark1`…`9`), top-bar layout and perspective/orthographic dropdowns, a draggable 3D/2D divider, and **Rendering mode (F10)** — a progressive path tracer on GPU compute (the repo claims ~25× CPU) or CPU, with environment importance sampling and an edge-aware denoiser.
### Bake and export
- **Bake mesh maps** — **10 baked maps**: normal, world-space normal, ID, ambient occlusion, curvature (also from the normal map), position, thickness, height, bent normal, opacity (`crates/core/src/bake/baked_map.rs` `BakedMap::ALL`). v0.3.0 also had `ColorMap` and `WorldDirection`; both are gone at v0.4.0, so do not request `colorMap`/`worldDirection` any more. GPU ray tracing (the repo claims ~12× the CPU path) or all CPU cores; high-poly meshes with cage, frontal/rear distances, average normals, match by mesh name; 2D cage and skew paint (`bake.cage.paint`/`bake.cage.reset`, `bake.skew.*`); mesh maps can also be imported as images (`textureSet.meshMap.set`). Fresh settings default to 64 AO rays, cosine distribution, 180° spread and linear AO attenuation (`crates/core/src/bake/ray_settings.rs`, `crates/core/src/bake/ambient_occlusion_attenuation.rs`).
- **Per-set bake settings**: each texture set owns a nested `BakeSettings` (common resolution/antialiasing/dilation plus per-map parameters). `bake.settings` reads or edits it: `model` replaces the whole settings object, `patch` changes only the named fields (nested `maps.ao.parameters.rays.rays`, …; `null` clears optional fields), `preserveEnabled` keeps the set's Include-in-Bake flag, and flat parameters (`resolution`, `antialiasing`, `dilation`, `ao`, `thickness`, `bentNormal`, `rays`, `maxDistance`, `curvatureMethod`, `positionMode`, `maps`, `enabled`, …) apply last. It returns `[{ts, settings, model}]` per targeted set (`crates/engine/src/bake/mesh_map_command.rs`). `bake.run` takes the same parameters plus `ts`/`cpu` and returns `{baked, tracer}`; **there is no auto-rebake at v0.4.0**.
- **Per-tile bake selection**: `bake.tile.set {ts?, tile: 1002, enabled: false}` excludes one UV tile; `session.inspect` reports each tile's `bakeEnabled`; excluded tiles keep their previous baked pixels (`crates/engine/src/bake/tile_command.rs`).
- **Export presets**: `Document channels + Normal + AO`, `glTF PBR Metal Roughness`, `Unreal Engine (Packed)`, `Unity HDRP (Mask Map)`, `Unity Standard (Metallic Smoothness)`, `Blender (Principled BSDF)`, `Godot 4 (ORM)`, `OpenPBR (separate maps)` (coat/fuzz/anisotropy/transmission/subsurface maps), `Separate maps`, `Mesh maps` — **10 built-ins** plus **user output templates** (per-channel packing, converted maps, JSON shareable) (`crates/formats/src/presets.rs`). Texture formats: PNG, JPEG, TGA, TIFF, BMP, WEBP, EXR, HDR — **depth is per format** (PNG/TIFF 8 or 16, EXR 16 or 32 float, HDR 32, the rest 8), plus UV padding, size override, OpenGL/DirectX normals, colour-space conversion of colour maps and `$udim` tiles (`crates/formats/src/export/mod.rs`). Mesh export to OBJ (+MTL and maps), textured GLB (clear coat + sheen) and USD (clear coat), **5 formats** (`crates/engine/src/file_cmds.rs` `MESH_EXPORT_FORMATS`).
## Known limitations and not-implemented
At v0.4.0, do not assign these:
- **No auto-rebake.** The `bake.autoRebake` commands of v0.3.0 were removed. After changing `bake.settings`, the mesh, high polys or the skew/cage paint, run `bake.run` yourself; nothing bakes in the background.
- **Old projects are recovered, not converted.** v0.4.0 writes `format_version: 2` (v0.3.0 wrote 1); older files load, newer files are refused (`crates/core/src/project.rs` `FORMAT_VERSION`, `crates/formats/src/project_file/mod.rs`). A `.tcraft` whose bake settings or baked map entries no longer deserialize opens with defaults for the unreadable fields (and drops unreadable maps, including the removed `ColorMap`/`WorldDirection`) under a log warning (`crates/formats/src/project_file/recovery.rs`); re-bake to restore those maps. Baked pixels of older UDIM projects keep their old layout — the app does not convert baked pixels.
- **`mesh_reimport` does not preserve paint by default.** Pass `preserveStrokes: true` (MCP/command) to re-project painted pixels where UVs changed; without it the mesh is replaced with no re-projection.
- **The macOS app bundle contains only `TextureCraft`** — no `texturecraft-cli`. Get the CLI from the release's separate macOS CLI zip (`packaging/macos/package.sh`), or build it. `Contents/MacOS` verified.
- **Pen tilt is not exposed** by the windowing layer (`ROADMAP.md` M2).
- **HDF5 Alembic is unsupported** (Ogawa only) and Alembic animation reads the first sample only (`ROADMAP.md` Project and files gaps).
- **Sheen and anisotropy do not export through USD** (UsdPreviewSurface has no such inputs; GLB and OpenPBR maps carry them) (`ROADMAP.md` Export gaps).
- **The bundled asset library is smaller than the reference app's**, and translations have had no native-speaker review (`ROADMAP.md` Materials/UI gaps).
- **Hardware ray tracing is not used** (compute shaders only) (`ROADMAP.md` Viewport gaps).
- The CLI/control/MCP surfaces run commands with defaults and never open dialogs; only `ui.menu.invoke` behaves like a click (`docs/control-protocol.md`). Bridge mode cannot touch files itself — the app does — so path tools are unavailable there and `project_save` without a path becomes File › Save.
## How to operate
- Headless one-shot (positional order: input first; `run` takes the command id then optional JSON params, with `-o` last):
`texturecraft-cli new builtin:material_ball out.tcraft` → `texturecraft-cli bake out.tcraft --resolution 1024 --maps ambientOcclusion,curvature` → `texturecraft-cli export out.tcraft textures/ --preset "glTF PBR Metal Roughness" --format png` → `texturecraft-cli render out.tcraft preview.png --size 1024x1024` (`apps/texturecraft-cli/src/main.rs`; `docs/features.md` §Built for agents).
- Script a session: `texturecraft-cli run project.tcraft layer.add.fill '{"color":"#336699","roughness":0.2}' -o result.tcraft` — commands are the same ids `commands --filter` lists. A batch script is a step list or `{"open", "steps", "save"}` with paths relative to the current directory.
- Edit bake settings precisely: `texturecraft-cli run out.tcraft bake.settings '{"ts":"all","patch":{"maps":{"ao":{"parameters":{"rays":{"rays":128}}}}}}'`; bake with overrides via `texturecraft-cli run out.tcraft bake.run '{"ts":"all","maps":["ambientOcclusion","curvature"],"resolution":2048}'`. Exclude a tile with `bake.tile.set {"tile":1002,"enabled":false}`.
- Cameras: `camera.add {"name":"Hero"}` saves the current view; `camera.bookmark1`…`9` (or `camera.select {name}`) jump to it; `texturecraft-cli render scene.tcraft hero.png --camera Hero` renders through it.
- Drive the live app: on macOS `open -a TextureCraft --args --control 7878` (the bundle has no CLI; `--args` apply on a fresh launch, so quit a running copy first), or launch the `texturecraft` binary with `--control 7878`; read the token from stderr or `<temp>/texturecraft-control-7878.token` (or set `TEXTURECRAFT_CONTROL_TOKEN`), send the `auth` line, then `command.run`/`session.inspect`/`viewport.screenshot`.
- Agent flows: `claude mcp add texturecraft -- texturecraft-cli mcp --read-root . --write-root .` (or a `.mcp.json`); bridge to the running app with `--bridge 7878`. Typical run: `project_new` → `command_run layer.add.fill` → `bake` → `command_run mask.add.black` → `command_run effect.add.generator {"generator":"edgeWear"}` → `session_inspect` → `viewport_render` → `project_save` → `textures_export` (`docs/control-protocol.md`).
- Verify without screenshots: `session.inspect` / `session_inspect` return project, mesh, texture sets (including each tile's `bakeEnabled`), layer stack, tool, camera, display, history; `bake.settings` returns the effective settings per set; `debug.frames` reports frame timing after a scripted stroke.
- `TEXTURECRAFT_NO_PREFS=1` isolates a scripted run from the user's saved layout/settings.
## Working with an AI agent
- Register the MCP server per `docs/control-protocol.md` (headless with roots, or bridged). In bridge mode the app reads and writes its own files, so path tools are unavailable and `project_save` without a path performs File › Save.
- Every tool call maps to one control method; `command_run` accepts any engine command id except the refused `file.*` set and path-taking commands. `command_list {filter}` is the discovery contract.
- Painted strokes and renders share one pixel space: coordinates for `paint_stroke` can be read off a `viewport_render` image at the default 1280×800.
- Baking is GPU-first with `cpu: true` fallback; use `bake.settings` `patch` for one parameter instead of hand-building a whole model, and always pass `ts` when more than one texture set must change.
- For reusable material libraries use the `.tcmat` round trip (`material_save`, then `material_load` above the selection); `material_load` also ingests MaterialCraft `.mcmat`/`.mcraft` packages.
- Treat a recovered project as untrusted until re-baked: check the log for the recovery warning, run `session.inspect`, re-bake the affected sets, then re-export.
- Reimport a changed mesh with `mesh_reimport {"path":"model.glb","preserveStrokes":true}` and confirm `session_inspect` reports `modified: true` before saving.
## Verification protocol
1. `texturecraft-cli commands --filter <text>` — the live registry for the build in front of you; never trust any static number in this skill over it.
2. `session.inspect` after each mutation — layer stack, mask targets, fill sources, tile `bakeEnabled`, baked maps, `modified` flag and painted tile counts.
3. Bake: `bake.settings` for the effective per-set model, `bake.run` for its `{baked, tracer}` result; `session.inspect` confirms the baked maps per texture set. Baked output is per-set and per-tile — check the tiles you meant to include.
4. Exports: `textures_export` returns the written files; check names, packing, per-format bit depth and UDIM tiles against the preset.
5. Frame-loop work (recordings, perf): `debug.frames` before/after; `strokeEnd` in its result names the frame a scripted stroke committed.
6. Re-derive counts at v0.4.0: engine registry **172**, MCP `tools/list` **22**, serve `methods` **17**, control methods **13**, CLI `--help` **10 subcommands**, menu parity **58/58** (`docs/parity.md`).
## Sources & evidence
- Repo: https://github.com/storytold/texturecraft — inspected at tag **v0.4.0**, commit **`16e9d21516434a63ac457f49ffd09005f649c02f`** (2026-11-06), inspection date **2026-11-07**, shallow checkout pinned to the tag; `git rev-parse HEAD` and `git describe --tags` both confirm v0.4.0. MIT OR Apache-2.0.
- Installed app: `/Applications/TextureCraft.app` — `TextureCraft 0.4.0 (16e9d21 2026-11-06)`, bundle `ai.storyteller.texturecraft`, `CFBundleShortVersionString` 0.4.0; `Contents/MacOS` holds only `TextureCraft` (no CLI); GUI never launched (`--version`/`--help` only).
- Read: `README.md`, `ROADMAP.md`, `AGENTS.md`, `Cargo.toml`, `docs/{control-protocol,features,parity,localization-parity}.md`, `apps/texturecraft-cli/src/main.rs`, `crates/automation/src/{server,rpc,workspace,security,budgets}.rs`, `crates/engine/src/{commands,filter_lib,smart_masks,camera_cmds,file_cmds}.rs`, `crates/engine/src/bake/{mesh_map_command,tile_command,paint_command,model_patch}.rs`, `crates/core/src/{channel,blend}.rs`, `crates/core/src/bake/`, `crates/formats/src/{presets.rs,export/mod.rs,mesh_import/mod.rs,project_file/recovery.rs}`, `crates/compose/src/eval.rs`, `crates/paint/src/{alphas,presets}.rs`, `crates/ui-egui/src/{control,i18n}.rs` and `crates/ui-egui/src/{toolbar,shelf,viewport}.rs`, `crates/geom/src/primitives.rs`.
- Counts measured from code, not docs: 172 engine command ids (full `cmd!`/`query!` extract), 22 MCP `#[tool]` entries, 17 serve `METHODS`, 13 control methods (+`auth`), 10 CLI subcommands, 58/58 menu items (`docs/parity.md`, generated + test-enforced), 31 `FILTERS` defs, 24 `SMART_MASKS`, 72 `m(` + 21 `sm(` materials, 23 `ProceduralKind`, 6 `ENVIRONMENTS`, 67 alpha-library entries (31 shapes + 36 glyphs), 18 brush presets, 21 `Channel` variants, 32 `BlendMode` variants, 10 `BakedMap`s (12 at v0.3.0; `ColorMap`/`WorldDirection` removed), 10 built-in export presets, 9 built-in meshes, 10 UI languages.
- Where repo docs disagree, the code counts win and the drift is noted: `ROADMAP.md` says 63 procedural materials, 30 filters and 19 channels (code: 72 / 31 / 21); `README.md`'s "8, 16 or 32 bits" shorthand is per-format (PNG/TIFF 8–16, EXR 16–32, HDR 32, others 8); `docs/control-protocol.md`'s CLI `--version` sample still shows an old 0.1.0 string.
- Per-claim tiering: `evidence.md` next to this file. Interfaces: **10 languages** — English, French, German, Spanish, Portuguese (Brazil), Italian, Japanese, Simplified Chinese, Korean, Arabic (`crates/ui-egui/src/i18n.rs`).