photocraft · git:20261026.76415fe · 2026-10-26 · sha256 b9e62b29b48161a4

photocraft git:20261026.76415feA

Immutable. This exact content never changes and is served at /api/v1/blob/b9e62b29b48161a4.

---
name: photocraft
description: PhotoCraft is the ArtCraft clean-room, pure-Rust reimplementation of Adobe Photoshop — layers, masks, adjustment layers, layer styles, live type, vectors, smart objects and real PSD read/write, with every action exposed as an engine command that the UI, a headless CLI, a JSON control channel and a 26-tool MCP server all drive. Trigger this skill whenever the user mentions PhotoCraft, asks what it can or cannot do, which file formats it reads or writes, its keyboard shortcuts, how to script, automate or batch it, how to prepare assets for it, how to inspect, verify or repair a .pcraft, PSD, XCF or .ora it produced, or how to troubleshoot it — and also when they never name the app but describe the job, such as "edit this photo", "open this PSD", "make a transparent PNG", "batch-resize a folder of images", "remove the background with a quick selection", "convert these HEIC/NEF/PCX files" or "check what is in this layered file". It is early alpha — never promise Photoshop-grade PSD fidelity, AI features or a stable UI, and prefer the headless CLI and MCP over driving the GUI blind.
---

# PhotoCraft

**Audit basis.** This skill was audited against tag **v0.16.0**, commit **`b1794c6e0608ccfc887213dcf58398977c5cb75e`** ("Release 0.16.0"), whose tree `git -C <clone> rev-parse HEAD` confirms. The installed `/Applications/PhotoCraft.app` reports `CFBundleShortVersionString` 0.16.0 (bundle id `ai.storyteller.photocraft`, minimum macOS 11.0) and its binary prints `photocraft 0.16.0 (b1794c6e0, 2026-10-25)`, so the bundle matches this source; the GUI was never launched. **Verify against your installed version** — the ArtCraft Launcher auto-updates, so a newer build may differ; re-check the counts below against your build (every count here was measured from code, not copied from the previous skill or from README/docs). No `references/` directory ships with this skill: everything is inline, and the per-claim evidence table lives in `evidence.md`.

## Overview

PhotoCraft is "Image editing; an open-source, clean-room reimplementation of Adobe Photoshop, rebuilt in pure Rust" (`README.md:17`), MIT/Apache-2.0 dual licence, `storytold/photocraft`. The native app is egui/eframe on wgpu with a CPU compositor kept as the reference oracle; the same engine and UI compile to WebAssembly (`apps/photocraft-web`, `[profile.wasm-release]` in `Cargo.toml`). Every menu item, tool and dialog is one engine command, so the UI, `photocraft-cli`, the loopback JSON control channel and the MCP server run the same code (`README.md:230`, `crates/engine/src/commands.rs:538` `build()`). Maturity is **early alpha**: feature breadth ~76% and "ready for real work" ~45% against Photoshop 2026, measured at 0.6.0 and not re-measured since (`ROADMAP.md:20-30`, which is itself stale — see Known limitations). All 631 Photoshop menu items are *wired* to a command (`docs/parity-checklist.md:5`, floor enforced at `crates/ui-egui/src/parity.rs:10`); wiring is dispatch, not behaviour.

## Capabilities (verified)

### Editing core

- **Documents and colour**: RGB, Grayscale, CMYK, Lab, Bitmap, Indexed, Multichannel, Duotone at 8/16/32-bit; ICC management in pure Rust with Assign/Convert to Profile, four rendering intents, black-point compensation, soft proofing and Gamut Warning (`README.md:205`, `crates/cms`, `crates/engine/src/color_cmds.rs` `specs()`).
- **Layers**: pixel layers, groups, clipping masks, pixel and vector masks with Density/Feather, fill layers (solid/gradient/pattern), adjustment layers, live smart objects with smart filters and lossless transforms/warps (including Cylinder), artboards, alpha channels, Quick Mask, layer comps, advanced blending (`crates/doc/src/lib.rs`, `crates/doc/src/effects.rs:277`).
- **16 adjustment layers**: `layer.newAdjustmentLayer.{brightnessContrast, levels, curves, exposure, vibrance, hueSaturation, colorBalance, blackWhite, photoFilter, channelMixer, invert, posterize, threshold, gradientMap, selectiveColor, colorLookup}` plus the same 16 as destructive `image.adjustments.*`, all generated from the 16-entry `ADJ` table (`crates/engine/src/commands.rs:1364-1450`). Extra destructive-only: Shadows/Highlights, Replace Color, Match Color, HDR Toning (`crates/engine/src/adjust_cmds.rs:537-575`).
- **10 layer effects**, several instances per kind: DropShadow, InnerShadow, OuterGlow, InnerGlow, Stroke, ColorOverlay, GradientOverlay, PatternOverlay, Satin, BevelEmboss, driven by 10 `layer.layerStyle.*` commands (`crates/doc/src/effects.rs:277`, `crates/engine/src/layer_style.rs` `style_cmd!`).
- **Blend modes**: PassThrough + 27 Photoshop modes + 6 Paint.NET-only modes = 34 enum variants (`crates/color/src/blend.rs:11-51`).
- **67 tools** — `pub const ALL: [Tool; 67]` (`crates/ui-egui/src/state.rs:209`), including Remove, Spot/Healing/Patch, Content-Aware Move, Mixer Brush, Magnetic Lasso, Object/Quick Selection, Pen/Freeform/Curvature Pen, Type and Type Mask, Perspective Crop, Frame, Slice. Of Photoshop's 68 classic tools only **Art History Brush** is missing (`docs/gaps.md` G6).
- **Selections**: marquee/lasso/wand, `select.quick`, `select.object`, `select.subject`, `select.colorRange`, `select.focusArea`, `select.refineEdge`, `select.magneticLasso`; Select and Mask is still a **dialog**, not a workspace (`crates/engine/src/smartselect_cmds.rs`, `docs/gaps.md` G7).
- **124 `filter.*` command ids**: 30 in `crates/engine/src/filters.rs:408` + 33 in `filters_ext.rs:595` + 47 Filter Gallery filters from the table in `crates/algo/src/artistic.rs` (each registered by `crates/engine/src/gallery_cmds.rs:174`, plus `filter.filterGallery` itself) + 13 workspace/lifecycle entries (`filter.blurGallery.{field,iris,path,spin,tiltShift}`, `filter.liquify`, `filter.adaptiveWideAngle`, `filter.cameraRaw`, `filter.vanishingPoint`, `filter.lensCorrection`, `filter.lastFilter`, `filter.convertForSmartFilters`). Filters applied to a smart object become re-editable smart filters; **54 PSD filter classes map to 49 distinct commands** through `filterFX` (`crates/io/src/smart_map.rs:155`).
- **Type**: point/area/paragraph text on canvas, Character/Paragraph controls, manual+optical kerning with PSD round trip, variable-font axes, `type.warpText`, `type.resolveMissingFonts`; headless editing via `type.editText`, `type.hitTest`, `type.caret`, `type.navigate`. Type on a path places glyphs rigidly only (`type.attachToPath`, `crates/engine/src/type_extra_cmds.rs`, `docs/gaps.md` G8).
- **Vectors**: Pen/Freeform/Curvature, anchor add/delete/convert, Paths panel, work paths, shape layers with dashed/aligned strokes and corner radii, path↔selection, SVG as editable shape layers or vector smart object (`crates/engine/src/vector_cmds.rs`, `docs/shape-strokes.md`).
- **Actions**: 16 `actions.*` commands (`list/get/record/stop/play/move/delete/toggle/rename/load/save/insert/dialog/assignSet/setColor/conditional`) with nesting to 16 levels and cycle rejection (`crates/engine/src/actions_cmds.rs:191`), plus **114 Photoshop event mappings** in the `EVENTS` table (`crates/engine/src/atn_cmds.rs:3405`) for `.atn` import/export; unmapped steps are kept verbatim and fail at playback by name.
- **Photomerge**: `file.automate.photomerge` (reposition/perspective layouts) as a cancellable background job (`crates/engine/src/photo_cmds.rs:900`).
- **Print**: File › Print renders one flattened page and prints through an in-process pure-Rust IPP client, the Linux print portal, the Windows spooler or to a PDF file; `file.printers` lists queues, `"dryRun": true` previews (`crates/engine/src/print_cmds.rs:15,1275`, `docs/printing.md`).
- **Video-lite**: 7 `timeline.*` commands (`create/delete/info/setProps/setFrame/nextFrame/previousFrame`, `crates/engine/src/timeline_cmds.rs:140`) and `file.export.renderVideo` (PNG sequence or animated GIF); animating GIF and WebP open every frame through New Video Layer from File (`crates/engine/src/video_cmds.rs`, `docs/file-format-parity.md` GIF/WebP rows).
- **Update check**: Help › Check for Updates plus an opt-out once-per-launch release-feed check, none inside Flatpak (`crates/engine/src/updates_cmds.rs:117`, `docs/scorecard.md` DIST-220-1 partial — **new since 0.12.0**, which had no update check).

### Files — read / write

- **Native `.pcraft`**: directory bundle or zip of STORE entries = `manifest.json` (format version **1**, `crates/format/src/manifest.rs:18`; migrations in `migrate.rs`) + content-addressed BLAKE3/zstd `tiles/`+`blobs/` + `thumb.png` + `composite/preview.png`; incremental, atomic (temp/fsync/rename, `crates/format/src/atomic.rs`), crash-recovery autosaves (`crates/format/src/autosave.rs`).
- **Read + write**: PSD/PSB/PSDT (`crates/io/src/psd_import.rs`, `psd_export.rs`, `crates/psd`), layered TIFF (`crates/io/src/tiff_layers.rs`), OpenRaster `.ora` (`crates/io/src/ora.rs`, `docs/ora.md`), PDF write-only one flattened page (`crates/io/src/pdf.rs`), and the flat codecs: PNG, JPEG, TIFF, WebP (lossless + own lossy VP8), GIF, BMP, TGA, ICO, Netpbm (PBM/PGM/PPM/PAM/PFM), QOI, DDS (uncompressed or BC1/BC2/BC3 + optional mip chain), OpenEXR, Radiance HDR, FITS, DPX, **Cineon (write added since 0.12.0 — `.cin` Saves now)** (`crates/codecs/README.md` matrix, `crates/codecs/src/format.rs:108` `Format::ALL [Format; 22]`, dispatch `crates/io/src/lib.rs:514` `export_stages`).
- **22 codec formats** in `Format` (`crates/codecs/src/format.rs:7`); every one is read+write except the documented asymmetries (`ASYMMETRIC_EXCEPTIONS`, `format.rs:73`): **read-only** PCX, XPM, DICOM and Amiga IFF (the 0.12.0 skill listed PCX, DICOM and IFF as unsupported; XPM it never mentioned), AVIF write-only behind the non-default `avif` feature, HEIF/HEIC read-only behind `heif` (on in official builds).
- **Layered imports (read-only)**: Paint.NET `.pdn`/PDN3 (editable layers, 14 blend modes), Krita `.kra`/`.krz`, **GIMP `.xcf` and `.xcf.gz` (new since 0.12.0**: pixel layers, groups, masks, blend modes, channels, saved selection, 8/16/32-bit, `crates/io/src/xcf.rs`, `docs/xcf.md`), Affinity `.af`/`.afphoto`/`.afdesign`/`.afpub` (`crates/affinity`), SVG (`crates/io/src/svg.rs`). Writing an import-only target is refused with a naming error in `export_stages` (`crates/io/src/lib.rs:516-532`).
- **Camera raw**: DNG (uncompressed/deflate/lossless-JPEG/lossy-JPEG/JPEG XL, CFA and LinearRaw), CR2, TIFF/EP raws (NEF, ARW, PEF), Nikon compressed NEF, Sony cRAW, Panasonic RW2, uncompressed Olympus ORF, uncompressed Fujifilm RAF (Bayer and X-Trans); measured profiles/image areas for Nikon D4/D5100/D90/D800E and Sony ILCE-7; everything else falls back to the embedded preview and **CR3 gives only its JPEG preview + orientation, not sensor data** (`crates/raw/src/lib.rs:34-41`, `cr3.rs:1-5`, `docs/file-format-parity.md` Camera raw).
- **Export As** offers exactly `png, jpg, webp, tif, tga, ico` (`crates/engine/src/export_preset_cmds.rs:13`); **Save for Web** offers `gif, png8, png24, jpeg, wbmp` (`crates/engine/src/web_cmds.rs:46-50`); Quick Export as PNG is a single menu item.
- **Honest exports**: `caps(format)` and `fidelity_warnings` report depth reduction, CMYK→RGB, dropped layers and missing fonts before writing; CLI/`info`/`app.save` surface them as warnings (`crates/codecs/README.md`).

### Automation surfaces (counts re-measured at v0.16.0)

- **One command registry, 631/631 menu items live** (`crates/ui-egui/src/parity.rs:10`, `docs/parity-checklist.md:5`). Total registry size: **~854 command specs** — a static count of 822 `spec!`/`cmd!`/`filter_cmd!`/`CommandSpec` registration sites in `crates/engine/src` (test files excluded) plus the 32 commands generated from the 16-entry `ADJ` table (`commands.rs:1364-1450`); this number is **uncertain by a few dozen** because two table macros (`style_specs!`, `gallery_specs!`) expand to several specs each — get the authoritative count with `photocraft-cli commands | wc -l`. README's "500+" (`README.md:230`) and `docs/architecture.md`'s "~700" are floors, not measurements.
- **CLI `photocraft-cli` — 8 subcommands**: `convert, info, run, batch, droplet, commands, mcp, serve` (`apps/photocraft-cli/src/args.rs:31-40`, dispatch `apps/photocraft-cli/src/lib.rs:48-62`), exit codes 0 ok / 1 failure / 2 usage. **9 export flags** shared by `convert`/`run`/`batch`: `--format --quality --tiff-layers --tiff-compression --tga-bits --dpx-bits --dds-compression --dds-mipmaps --jpeg-format` (`args.rs:44-83`); **new since 0.12.0**: `--tiff-compression none|lzw|zip`, `--jpeg-format baseline|optimized|progressive`, and `--in-place` on `convert` and `run` (previously only `batch`). New also: shell completions (bash/zsh/fish/elvish/PowerShell) and man pages per subcommand, generated by `apps/photocraft-cli/examples/man-and-completions.rs` and shipped in the Linux/FreeBSD packages.
- **Control channel** (`photocraft --control <port>`, loopback only): first frame must be `{"id":"auth","method":"auth","params":{"token":"<64 hex>"}}`; then **28 methods** — `engine.execute`, `engine.commands`, `jobs.list`, `jobs.cancel`, `ui.inspect`, `ui.set`, `ui.menu.invoke`, `ui.menu.list`, `ui.pointer`, `ui.click`, `ui.move`, `ui.key`, `ui.type`, `ui.resize`, `ui.focus`, `ui.screenshot`, `ui.context.choose`, `ui.dialog.open/set/confirm/apply/cancel`, `ui.window.open/close`, `ui.gpu.simulateLoss`, `app.open`, `app.save`, `app.quit` (`crates/ui-egui/src/control.rs` dispatch; `docs/control-protocol.md` "Methods"). Transports: `apps/photocraft/src/control_server.rs`; 60 s reply timeout, 1 MiB request cap, 16 connections (`crates/automation/src/security.rs:15-29`). Same count as the previous skill, which listed them correctly.
- **MCP — 26 tools** (`crates/automation/src/server.rs:610-1016`): `session_list`, `doc_open`, `doc_place`, `doc_replace_smart_object`, `doc_export_layers`, `doc_export_data_sets`, `doc_import_data_sets`, `doc_new`, `doc_save`, `doc_export`, `doc_inspect`, `doc_render_preview`, `render_preview`, `doc_select`, `doc_close`, `command_list`, `command_run`, `jobs_list`, `jobs_cancel`, `command_batch`, `ui_inspect`, `ui_screenshot`, `ui_pointer`, `ui_menu_invoke`, `ui_set`, `control_call`. **New since 0.12.0**: `doc_replace_smart_object` (and `doc_render_preview` is a distinct registry entry the old skill never enumerated). Bridge-only: the five `ui_*` tools + `control_call`; headless-only: `doc_select`, `doc_close`. **2 MCP resources** (`photocraft://document`, `photocraft://commands`, `crates/automation/src/server/conventions.rs:7-8,63-90`) — a surface the previous skill omitted entirely.
- **Headless server** `photocraft-cli serve` (stdio or `--port`) answers **19 methods** (`crates/automation/src/rpc.rs:42-61` `METHODS`): `engine.execute`, `jobs.list`, `jobs.cancel`, `engine.commands`, `session.list`, `doc.open/place/replaceSmartObject/new/save/exportLayers/exportDataSets/importDataSets/inspect/render/select/close`, `batch`, `methods` — one more than 0.12.0 (`doc.replaceSmartObject`).
- **Automation budgets** (measured, `crates/automation/src/budgets.rs:13-19`): 8 MiB reply, 5 MiB PNG, preview `max_side` 2048, 64 M source pixels for renders, 256 batch steps (`security.rs:21`), 30 s IO timeout, 10 s auth deadline.
- **Plug-ins**: WebAssembly **ABI v1** only — `plugin.list/install/remove/reload/run` (5 commands, `crates/engine/src/plugin_cmds.rs:19,273-319`), wasmi sandbox, no WASI/filesystem/network/clock, 32 MiB module, 512 MiB memory, 50 M instructions + 4,000 per sample, 60 s per run (`docs/plugins.md`, `crates/plugins/src/runtime.rs:14,45-47`).
- **Batch/droplets**: `photocraft-cli batch` takes an actions JSON, an Actions set (`--action` picks by name) or a `.atn` set, applies it to a folder with `{name}/{serial}/{date}` output templates, and refuses `--out == --in` unless `--in-place` (`apps/photocraft-cli/src/args.rs:155-176`, `batch.rs:43-60`, `docs/batch-naming.md`). `.pcdroplet` droplets run via `file.automate.runDroplet`.

### Platform

- macOS (universal, signed+notarized), Windows x64/x86/ARM64, Linux x86_64/aarch64 (AppImage, Flatpak, deb, rpm, tarball, riscv64 tarball), FreeBSD 14 x86_64, and a WebAssembly web build (`README.md` Downloads; `docs/scorecard.md` DIST rows — Windows signing is skipped with a warning until the signing material exists, and the FreeBSD tarball layout is checked but not yet produced by a release run).
- GPU compositor on wgpu (Metal/Vulkan/DX12/WebGPU) with CPU fallback, `--safe-gpu`, automatic device-loss fallback, and `ui.gpu.simulateLoss` (with `destroy: true`) for testing (`apps/photocraft/src/gpu_startup.rs`, `docs/control-protocol.md`).
- Pen tablets (`docs/hardware-parity.md` Pen tablets, `crates/tablet`): macOS pressure/tilt/barrel-rotation (rotation not applied to tip angle, #1301); Windows **pressure only** (tilt and rotation missing, #2046/#2285); Linux X11 pressure+tilt with partial rotation; web pressure/tilt/twist; native Wayland tablet-v2 still open.

## Known limitations and not-implemented

Read this before assigning work — several of these are why agents must not promise Photoshop behaviour.

- **Alpha, by measurement**: ~45% "ready for real work", 953 open issues as of the ROADMAP's 2026-10-13 measurement (`ROADMAP.md:41-45`); `docs/gaps.md` G3 quotes **579** open issues at 2026-10-10 — the two documents disagree, trust neither number exactly and check GitHub. Performance: 64 scenarios, **3 within budget, 22 over budget or failing**, 9 tracked only, 26 not run, 4 not measurable (`docs/scorecard.md:9`). The ROADMAP still says menus 628/628, tools 56/68 and formats "13+4 of 31" — the generated parity checklist says 631/631 and the code has 67 tools (`docs/gaps.md` G6), so ROADMAP headline rows are stale.
- **PSD fidelity is the blocking gap (G1)**: enforced corpus floors are *minimums* — Photoshop-authored oracles **190/256** render, psd-tools 249/309, io corpus 148/170, round trips 258/256, 308/309, 169/170 (`crates/io/tests/it/corpus.rs:151-159`; note the Photoshop oracle set is 256 or 258 files depending on the row — the floor is what CI enforces). A re-saved PSD is **never byte-identical** to its source (only the standalone `photocraft-psd` crate reproduces corpus files byte for byte), and Photoshop failing to reopen our files is tracked in #1281/#2469 (`docs/gaps.md` G1).
- **No AI/generative features**: no ONNX runtime or `ml` crate in the workspace; no Neural Filters, generative fill/expand, sky replacement or generative Remove. Classical substitutes only (Select Subject via GrabCut/max-flow, Remove by patch completion) (`docs/gaps.md` G15, `docs/scorecard.md` AUTO-219-7 missing).
- **No native Photoshop plug-ins**: `.8BF`, CEP and UXP are not hosted; only the Wasm ABI v1 (`docs/gaps.md` G16, `docs/plugins.md`; ABI v2 with panels/progress is `docs/scorecard.md` AUTO-219-6 missing).
- **Workspaces that are dialogs (G7)**: Select and Mask is a dialog, there is no Content-Aware Fill sampling workspace, Vanishing Point has a plane editor but no clone-in-plane, and **Path Blur is still a parameter dialog** while Field/Iris/Tilt-Shift/Spin are on-canvas (`docs/gaps.md` G7).
- **Type depth (G8)**: type on a path places glyphs rigidly; no RTL visual caret or PSD paragraph-direction storage; no OpenType Numerals/Stylistic Sets submenus; text free transform missing (`docs/gaps.md` G8).
- **Formats absent**: Photoshop PDF **read** (#2541); EPS/DCS and generic EPS/AI; Cloud PSD `.psdc`; JPEG 2000; Pixar; WBMP; MPO/JPS; PICT; Scitex CT; Photoshop Raw; AVIF read; JPEG XL only inside DNG. Affinity, Paint.NET, Krita and XCF are **import-only** (writable targets are refused in `crates/io/src/lib.rs:516-532`). File › Open takes only a GIF/WebP animation's first frame (use New Video Layer from File for all frames) and per-frame delays are not kept in `.pcraft` (`docs/file-format-parity.md`).
- **Camera raw partial**: CR3 sensor data no (preview only), "lossy after split" NEF no, no `.xmp` sidecars, no Fuji colour calibration, Olympus ORF and Fujifilm RAF only uncompressed (`crates/raw/src/lib.rs`, `docs/file-format-parity.md` Camera raw).
- **Dead preferences**: 20 of 177 settings do nothing (`docs/scorecard.md:12,188`; was 24 of 171 at 0.12.0). CI regenerates that count, so re-read the scorecard rather than trusting this line.
- **Automation is fail-closed**: these 14 commands are refused over control/MCP/`serve` — `file.open`, `file.openAs`, `file.save`, `file.saveAs`, `file.saveACopy`, `file.export.saveForWebLegacy`, `pattern.import`, `layer.smartObjects.{exportContents, replaceContents, relinkToFile, convertToLinked}`, `measurementLog.export`, `layer.videoLayers.reloadFrame`, `edit.colorSettings`; path params of other commands (`layer.exportAs`, `filter.distort.displace` `mapPath`, preset imports, plug-in install, `layer.setAdjustment {"file": …}`, `prefs.set` on file-backed sections) are refused too, and the **desktop app additionally refuses `image.mode.*`** (`crates/automation/src/workspace.rs:745` `filesystem_commands_fail_closed`; 14 entries now vs ~10 at 0.12.0 — `layer.smartObjects.convertToLinked` and `layer.videoLayers.reloadFrame` were added).
- **Localization**: UI strings translated, engine messages stay English; Hindi, Arabic (RTL shell only) and Vietnamese are 0% (`ROADMAP.md` Languages, `docs/gaps.md` G13).
- **Distribution gaps**: no in-app bug-report dialog (DIST-220-3 missing), no periodic update recheck while the app stays open (DIST-220-1 partial), Windows signing skipped without secrets (DIST-2 partial) (`docs/scorecard.md`).
- **Wayland**: no drag-and-drop (winit 0.30 limit) and XWayland fractional scaling is blurred (`README.md:289`).
- **Docs lag the code** — several generated/hand-written docs contradict this tag's source: `docs/file-format-parity.md`'s "ours only" table still lists Krita and GIMP XCF as unread although `crates/io/src/kra.rs`/`xcf.rs` import them; `docs/gaps.md` says 112 `.atn` events while the table has 114; the ROADMAP headline rows are pre-0.10. Verify against your installed version before asserting a feature is missing.

## How to operate

### Launch (macOS)

```sh
open -a PhotoCraft                        # normal GUI launch
open -a PhotoCraft /path/to/file.psd      # open a file in the running instance
open -a PhotoCraft --args --control 7878 --control-token-file "$HOME/.photocraft/control.token" \
     --automation-read-root "$PWD" --automation-write-root "$PWD"
```

- Binary: `/Applications/PhotoCraft.app/Contents/MacOS/PhotoCraft`. The bundle contains **no CLI** — get `photocraft-cli` from `photocraft-cli-<ver>-macos-universal.zip` (signed, notarized), `cargo install --path apps/photocraft-cli --locked`, or the Linux `.deb`/`.rpm`, which install `photocraft-cli` on `PATH`.
- App flags, parsed by clap so a typo is a usage error (exit 2): files, `-h/--help`, `--version`, `--safe-gpu`, `--in-window-menus`, `--new-instance`, `--present-mode <fifo|vsync|autovsync|novsync|immediate|mailbox|autonovsync>`, `--frame-latency <1..3>`, `--stroke-trace <stderr|FILE.csv>`, `--control <port>`, `--control-token <64-hex>`, `--control-token-file <path>`, `--automation-read-root <dir>`, `--automation-write-root <dir>`, `--` (`apps/photocraft/src/launch_args.rs:27-74`). Env equivalents: `PHOTOCRAFT_CONTROL_PORT`, `PHOTOCRAFT_CONTROL_TOKEN(_FILE)`, `PHOTOCRAFT_AUTOMATION_{READ,WRITE}_ROOT`, `PHOTOCRAFT_CONFIG_DIR`, `PHOTOCRAFT_NEW_INSTANCE`.
- Without a token, a fresh 256-bit one is generated and printed to stderr; a token file is created mode 0600. Treat it as a credential: it grants the whole non-filesystem control surface over unencrypted loopback (`crates/automation/src/security.rs:15-29`, `docs/control-protocol.md`).
- Never launch the GUI just to inspect a file — use the CLI. On macOS occluded windows stop rendering, so `ui.screenshot` raises the window and steals focus (`ui.screenshot {focus:false}` avoids it; `docs/development.md`).

### Headless CLI

```sh
photocraft-cli convert in.psd out.pcraft [--format <ext>] [--quality 1-100] [--tiff-layers] \
    [--tiff-compression none|lzw|zip] [--jpeg-format baseline|optimized|progressive] \
    [--tga-bits 24|32] [--dpx-bits 8|10|12|16] [--dds-compression none|bc1|bc2|bc3] [--dds-mipmaps] [--in-place]
photocraft-cli info out.pcraft [--compact]              # JSON: size, mode, depth, layer tree, warnings
photocraft-cli run in.png --cmd layer.new.layer --params '{"name":"Ink"}' \
                        --cmd filter.blur.gaussianBlur --params '{"radius":3}' \
                        --out out.psd <same export flags>
photocraft-cli run --new '{"width":800,"height":600,"background":"white"}' --cmd document.inspect --out new.pcraft
photocraft-cli batch --actions actions.json [--action <name>] --in ./raw --out ./graded [--format jpg] [--in-place]
photocraft-cli droplet job.pcdroplet ./images [--out ./done]
photocraft-cli commands [--json] [--filter blur]
photocraft-cli mcp [--bridge 127.0.0.1:7878] [--control-token <hex> | --control-token-file <path>] \
                   [--automation-read-root <dir>] [--automation-write-root <dir>]
photocraft-cli serve [--port <port>] [--control-token <hex> | --control-token-file <path>] \
                   [--automation-read-root <dir>] [--automation-write-root <dir>]
```

Behaviour that bites: each `--params` belongs to the `--cmd` before it; `run` needs exactly one of `<FILE>` or `--new`, and at least one `--cmd` or `--out`; command results are one JSON line each on stdout while warnings go to stderr; `convert`/`run` refuse an `<OUT>` that is the `<IN>` unless `--in-place`; `batch` refuses an `--out` equal to `--in` (canonicalised, so symlinks count), refuses to overwrite a same-named output, and fails the file whose nested action fails; `--quality` sets JPEG and lossy WebP (without it WebP is lossless); MCP `--bridge` and `serve --port` refuse automation roots/token flags they cannot honour (`apps/photocraft-cli/src/args.rs:365-420`, `batch.rs:43-60`).

### Document and window model

One window holds the open documents as tabs (the control channel exposes them as `window.document.<index>` in tab order, active one checked); `ui.window.open` adds an extra window. On Windows and Linux a second launch hands its files to the running instance as new tabs; `--new-instance` and any `--control` launch start a separate process (macOS relies on LaunchServices) — `apps/photocraft/src/single_instance.rs`, `docs/control-protocol.md`.

### Open / save / export flow

- Open: File › Open (`Cmd+O`), drag-and-drop, `open -a PhotoCraft <file>`, `photocraft-cli`, or `app.open`/`doc.open` over automation. Save (`Cmd+S`) writes back to the document's own PSD/PSB/`.pcraft`; Save As (`Cmd+Shift+S`) picks the format from the extension — `.pcraft` for lossless native, PSD/PSB for Photoshop handoff, any writable codec extension for flat output, `.pdf` for one flattened page, `.cin` for Cineon, `.dds` with DDS Options. Long opens/saves run as cancellable background jobs with progress (`docs/control-protocol.md` "Background jobs").
- Over the control channel/MCP use `app.open`/`app.save` (desktop) or `doc.*` (serve/MCP) with paths **relative to the automation roots**; the refused ambient commands are listed above. Embedded smart objects are the exception that works: `layer.smartObjects.editContents`, `saveContents`, `convertToLayers` run pathless, and the capability-scoped `doc_place` (MCP), `doc.replaceSmartObject` (serve) and `doc_replace_smart_object` read beneath the read root (`docs/mcp.md:112-116`, `docs/control-protocol.md:404`).
- Modal workspaces gate menu invocation: while Camera Raw, Liquify, Content-Aware Fill, Filter Gallery or Adaptive Wide Angle is open, `ui.menu.invoke` refuses every other item and accepts only that workspace's `{"ui":{…}}` requests — finish with `{"ui":{"commit":true}}` or `{"ui":{"cancel":true}}` first (`docs/control-protocol.md` "Methods"). `engine.execute` is *not* gated.
- Keyboard shortcuts: authoritative tables are `crates/engine/src/shell_commands.rs` (shell + tool defaults) and `crates/ui-egui/src/menu_catalog.rs` (menu items). Verified examples: `Cmd+O`/`Cmd+S`/`Cmd+Shift+S`, `Cmd+Alt+Shift+W` Export As, `Cmd+F` Search…, `Cmd+J` Layer via Copy, `Cmd+D` Deselect, `F5` Brush Settings, `F6` Color, `F7` Layers. Photoshop `.kys` sets import/export through `edit.keyboardShortcuts` (including a one-time offer to import Photoshop's live installed set) — `crates/engine/src/kys.rs`, `docs/control-protocol.md:236`.

### State on disk

- Settings: macOS `~/Library/Application Support/Photocraft/` (`preferences.json`, `Presets/` with `.pcbrushes`, `actions.json`, gradient/style presets, `ui.ron` for window and dock geometry); Windows `%APPDATA%\Photocraft`; Linux `$XDG_CONFIG_HOME/photocraft` or `~/.config/photocraft`. Override with `PHOTOCRAFT_CONFIG_DIR`; portable mode (`portable.txt` or `PhotoCraft.portable` beside the executable) keeps everything in `<exe dir>/PhotoCraftData` (`apps/photocraft/src/app_dirs.rs:7-22`).
- Crash recovery: `<data dir>/Recovery/<key>.pcraft` directory bundles plus `<key>.json` metadata, offered at the next launch and deleted when the document is saved or closed. Autosave interval is preference `fileHandling.autosaveMinutes`, default 10, valid 1–240 (`crates/format/src/autosave.rs`, `crates/engine/src/prefs.rs:1205`).

## Working with an AI agent

Preferred ladder — use the highest rung that answers the question:

1. **Headless CLI** for convert/inspect/batch; no GUI, no token (`photocraft-cli convert|info|run|batch|droplet|commands`).
2. **MCP headless** (`photocraft-cli mcp --automation-read-root … --automation-write-root …`) for command sequences plus inspection; without roots it has no file authority (by design).
3. **Control channel on a live app** (`--bridge`, or `photocraft --control`) when UI state, dialogs, pointer gestures or screenshots are genuinely required.
4. **GUI pointer events last** — `ui.pointer`/`ui.key` are validated and replayable but depend on window focus on macOS.

Legitimate work:

- **Prepare inputs**: convert any supported source to `.pcraft`/PSD/PNG (or DDS textures with `--dds-compression`/`--dds-mipmaps`), resize/grade via `batch`, generate actions JSON or droplets, pre-export assets for other Craft apps. HEIC, NEF/CR2/DNG, XCF, KRA, PDN and Affinity files are *sources*, not deliverables.
- **Read and interpret documents**: `photocraft-cli info <file>` JSON, or `document.inspect`/`layer.list`/`document.pixel` over MCP; `.pcraft` `manifest.json` is plain JSON and can be read directly.
- **Verify exports**: reopen with `photocraft-cli info`, read stderr warnings, probe pixels, compare size/alpha. Do not byte-compare a re-saved PSD to its source — PhotoCraft rewrites image resources, layer records and the composite, so only reopening the file tells you it is right.
- **Repair by regeneration, not by hand**: `.pcraft` tiles/blobs are content-addressed zstd objects — never edit them in place. Rebuild from the source image, an earlier bundle or the app; a bundle with a newer `format_version` is refused with a clear error (`crates/format/src/migrate.rs`).
- **Batch safely**: `batch` will not silently overwrite and fails a file whose nested action fails; MCP `command_batch` is **not** atomic (earlier steps stay applied, no rollback), so inspect history afterwards and undo per entry. Long steps accept `wait: false` and are polled/cancelled with `jobs.list`/`jobs.cancel`.

Do not: assign AI/generative work, expect Photoshop to always open our PSD exports, assume a format is supported because Photoshop has it, drive the GUI blind when a headless command exists, use `image.mode.*` from desktop automation, assume XCF/KRA/PDN/Affinity files can be saved back, or trust doc statements that the code contradicts.

## Verification protocol

PhotoCraft is alpha: verify every artefact, never trust a silent export.

1. **Round-trip smoke test** in a scratch directory: `photocraft-cli run --new '{"width":64,"height":64,"background":"white"}' --cmd layer.new.layer --out t.pcraft`, then `photocraft-cli info t.pcraft`, then `photocraft-cli convert t.pcraft t.png` and open the PNG in another viewer. For edits, insert a command between `--new` and `--out` and read each command's JSON line.
2. **Read the warnings.** Depth reduction, CMYK→RGB, dropped layers, clipped values and missing fonts print to stderr on `convert`/`run`/`batch` and appear in `info`/`app.save` `warnings`. An export with no warning is the only export you should believe.
3. **Version projects.** Keep the `.pcraft` for lossless re-edits and hand Photoshop the PSD/PSB; keep the original source. Re-saved PSDs are not byte-identical, and `.pcraft` migrates on load — never downgrade a bundle.
4. **Update only between projects.** This skill documents 0.16.0 (`v0.16.0`, commit `b1794c6e0`); a launcher auto-update can change how files save. If the installed app differs, re-verify formats and counts (`photocraft --version` prints version, commit and build date — `crates/engine/src/build_info.rs:32`).
5. **Prefer reversible operations.** Work on copies (`image.duplicate`, or `history.newDocument` which creates a new active document by default), inspect `document.inspect` history, and remember automation saves never fire script events.

## Sources & evidence

- Repo: https://github.com/storytold/photocraft — inspected at tag **`v0.16.0`**, commit **`b1794c6e0608ccfc887213dcf58398977c5cb75e`**, inspection date **2026-10-26**. The clone is shallow (one commit, "Release 0.16.0"), so no `v0.12.0…v0.16.0` diff exists locally; the deltas below were derived by re-measuring this checkout and comparing with the previous skill and the repo's own revision histories (`docs/file-format-parity.md:141-180`, `docs/gaps.md:3`, `docs/scorecard.md`).
- Installed app: `/Applications/PhotoCraft.app` — version 0.16.0, bundle id `ai.storyteller.photocraft`, minimum macOS 11.0, `Contents/MacOS/PhotoCraft` the only binary (no CLI, no `Resources/bin`) (`Contents/Info.plist`); the app was not launched.
- Key files read: `README.md`, `ROADMAP.md`, `Cargo.toml`, `AGENTS.md`, `apps/photocraft/src/launch_args.rs`, `app_dirs.rs`, `control_server.rs`, `single_instance.rs`, `gpu_startup.rs`, `apps/photocraft-cli/src/{lib,args,document,batch,automation,actions,output}.rs`, `apps/photocraft-cli/examples/man-and-completions.rs`, `crates/engine/src/{commands,atn_cmds,actions_cmds,adjust_cmds,filters,filters_ext,gallery_cmds,layer_style,plugin_cmds,print_cmds,photo_cmds,timeline_cmds,video_cmds,export_preset_cmds,web_cmds,prefs,build_info,updates_cmds,kys}.rs`, `crates/ui-egui/src/{state,parity,control,menu_catalog}.rs`, `crates/doc/src/effects.rs`, `crates/color/src/blend.rs`, `crates/algo/src/artistic.rs`, `crates/codecs/src/format.rs`, `crates/codecs/README.md`, `crates/io/src/{lib,psd_import,psd_export,xcf,kra,pdn,affinity,ora,tiff_layers,pdf,svg,smart_map}.rs`, `crates/io/tests/it/corpus.rs`, `crates/format/src/{manifest,migrate,atomic,autosave}.rs`, `crates/raw/src/{lib,cr3}.rs`, `crates/plugins/src/{runtime,manifest}.rs`, `crates/automation/src/{server,server/conventions,rpc,security,budgets,workspace,args,bridge,headless}.rs`, `docs/{control-protocol,mcp,scorecard,gaps,roadmap,parity-checklist,file-format-parity,plugins,printing,batch-naming,ora,xcf,kra,pdn,smart-filters,shape-strokes,hardware-parity}.md`.
- Shared libraries: `storytold/craft-libs`, pinned by git revision in `Cargo.toml` (`craft-ui` rev `5c874d51…`, `craft-print` rev `3d37da38…`, `craft-docpath` rev `82a24b00…`, `craft-jpeg` rev `6e7f2851…`); only the pinning relationship was consulted, not shared-library behaviour.
- Per-claim evidence and the automation-surface count deltas: `evidence.md` next to this file.