photocraft · diff

git:20261101.18741c7 to git:20261102.c601545

9 added, 7 removed. Audit A to A.

---
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, generative 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.18.0**, commit **`8a87a457abb43ac344deeb3fdb67541b91a72ff1`** ("Release 0.18.0", 2026-10-31), whose tree `git -C <clone> rev-parse HEAD` confirms. The installed `/Applications/PhotoCraft.app` still reports `CFBundleShortVersionString` 0.17.0 (bundle id `ai.storyteller.photocraft`, minimum macOS 11.0) and its binary prints `photocraft 0.17.0 (b8c3f3aa8, 2026-10-28)` — **the installed bundle lags this tag**; 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`.
+ **Audit basis.** This skill was audited against tag **v0.19.0**, commit **`2df9f807702a935184041355a86405380bda8274`** ("Release 0.19.0", 2026-11-01; the v0.18.0 ref was fetched for diffs), whose tree `git -C <clone> rev-parse HEAD` confirms. The installed `/Applications/PhotoCraft.app` matches this tag (bundle id `ai.storyteller.photocraft`, minimum macOS 11.0) and its binary prints `photocraft 0.19.0 (2df9f8077, 2026-11-01)`; the GUI was never launched, but the bundle's `photocraft-cli` was run read-only (`commands`, `--help`). **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`.
+ v0.18.0 → v0.19.0 adds a **new command family, `smart_svg_cmds`** — **edit embedded vectors with original-format save-back through VectorCraft** (PR #8571; `crates/engine/src/cmd.rs` wires `smart_svg_cmds::specs()`), reworks `edit.clear`'s targeted-channel/Quick-Mask handling inline, and keeps the registry shape otherwise (MCP, control channel and CLI: zero diff files; 9 CLI subcommands re-verified live). The live registry count from the installed matching bundle is **953 commands** (`photocraft-cli commands | wc -l`). What is new: **PSD font metadata survives text edits** (#6169); the **Move tool no longer copies a huge layer for every frame of a live drag** (#6985); **`.atn` steps can switch to another open document** (#7621) and action export targets are hardened (#8371); **Layer Panel Footer gains a Pattern Fill option** (#7716); **advanced selection keyboard shortcuts** (#8015); a paragraph box that can't hold its text draws a **red + overflow port** (#8311); **TIFF images over 2^30 px open, decoded a group of rows at a time** (#8536); **multi-layer selections get transform handles** (#8623); a **Photoshop-style Fill/Stroke popover and the full Color Picker open from every colour swatch** (#8725); **selected colour-channel sets are editable** (#9274); the text and gradient-stop Color Pickers show a **live canvas preview** (#9757); brush state survives a failed preset-tip load (#10288); plus an integration batch of 18 PRs (#10145). Interface catalogs stay at 26 (the release notes' Finnish and Norwegian Bokmål PRs were already in v0.18.0's 26 — verified against both tag trees).
+
v0.17.0 → v0.18.0 is a **large integration wave with the registry shape unchanged** (the `build()` table gained no new rows; the authoritative count stays `photocraft-cli commands | wc -l`). What is new: **`photocraft-cli` ships inside the macOS app bundle** (`Contents/MacOS/photocraft-cli`, PR #4742) and droplet scripts resolve it from there — the bundle-is-no-CLI caveat in How to operate is gone for macOS; **Video Frames to Layers opens every page of a multi-page TIFF** (#6379); **Smart Object sources composite through the strip compositor** with a bounded, purged decode cache (embedded contents count toward the history budget); **Filter Gallery › Cutout is a real 4-stage implementation** (tone bands, flat regions, edges, curves — `crates/algo/src/cutout/`); **Gaussian blur is described as data for other devices** (#8931); **Automation resolves configured root aliases for absolute paths** (#6426); **Shift+right-click opens a brush mode picker and every painting tool's own Mode list** (#4985); **Pen: Path Operations menu** (#7648); **Smart Filters: double-clicking a Liquify filter reopens the Liquify workspace** (#7153); duplicate native layer identities are rejected (#5870); native documents opened at launch report in the interface language; a **startup report** is written (`apps/photocraft/src/startup_report.rs`); and the interface gained **Finnish** (locale catalogs 21 → 26).
## Overview
PhotoCraft is "Image editing; an open-source, clean-room reimplementation of Adobe Photoshop, rebuilt in pure Rust" (`README.md`), MIT/Apache-2.0 dual licence, `storytold/photocraft`. The native app is egui/eframe on wgpu (migrated to egui 0.37 with the shared `craft-ui` docking in this release) with a CPU compositor kept as the reference oracle; the same engine and UI compile to WebAssembly (`apps/photocraft-web`). 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 (`crates/engine/src/cmd.rs` `build()`). Maturity is **early alpha**; `docs/gaps.md` (last updated 2026-10-27) is the ranked shortfall list against Photoshop 2026. All 631 Photoshop menu items are *wired* to a command (`docs/parity-checklist.md:5`, floor enforced at `crates/ui-egui/src/menus/parity.rs`); 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 (`crates/cms`, `crates/engine/src/cmd.rs` adjustments, CMYK compositing on stored inks).
- **Layers**: pixel layers, groups (new-group dialog; new layers join an open group and its clip group), 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 (New Document can start as an artboard; Artboard from Layers takes a custom size), alpha channels (drag-reorder in the Channels panel), Quick Mask, layer comps, advanced blending with per-layer Transparency Shapes Layer (`crates/doc/src/lib.rs`, `crates/doc/src/effects.rs`).
- **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/cmd.rs:1093`). Extra destructive-only: Shadows/Highlights, Replace Color, Match Color, HDR Toning; 32-bit Hue/Saturation and Colorize behave as in Photoshop.
- **10 layer effects**, several instances per kind: DropShadow, InnerShadow, OuterGlow, InnerGlow, Stroke, ColorOverlay, GradientOverlay, PatternOverlay, Satin, BevelEmboss (separate Highlight/Shadow mode dropdowns), driven by `layer.layerStyle.*` commands (`crates/doc/src/effects.rs`, `crates/engine/src/layer_style.rs`); `.asl` style libraries import.
- **Blend modes**: PassThrough + 27 Photoshop modes + 6 Paint.NET-only modes = 34 enum variants (`crates/color/src/blend.rs`); Shift+Alt+letter picks a blend mode from the keyboard.
- **68 tools** — `pub const ALL: [Tool; 68]` (`crates/ui-egui/src/state/tool.rs:82`), **every classic Photoshop tool is now present**: Art History Brush landed in this release (upstream marks it partial, TOOL-213-20, `docs/gaps.md` G6). Includes Remove, Spot/Healing/Patch, Content-Aware Move, Mixer Brush (renders mixed paint live during the stroke), Magnetic Lasso, Object/Quick Selection, Pen/Freeform/Curvature Pen, Type and Type Mask, Perspective Crop, Frame, Slice; mode dropdowns for Clone/Pattern Stamp, History Brush, Healing and Paint Bucket.
- **Selections**: marquee/lasso/wand, `select.quick`, `select.object`, `select.subject`, `select.colorRange`, `select.focusArea`, `select.refineEdge`, `select.magneticLasso`; Select and Mask refinement is guided-filter matting with Smooth/Feather/Contrast/Shift Edge and colour decontamination (`crates/algo/src/matting.rs`) — classical, no learned models; Select and Mask stays a **dialog**, not a workspace (`crates/engine/src/cmd/cutout_cmds.rs`, `docs/gaps.md` G7). Remove Background (Properties › Quick Actions) turns Select Subject + edge refinement into a layer mask in one history step.
- **130 `filter.*` command ids**: 66 registered pairwise (32 in `crates/engine/src/filters.rs` + 34 in `filters_ext.rs`) + 47 Filter Gallery filters from the `gallery!` table in `crates/algo/src/artistic.rs:38` (each registered by `crates/engine/src/cmd/gallery_cmds.rs`, plus `filter.filterGallery` itself) + 17 workspace/lifecycle ids (`filter.blurGallery.{field,iris,path,spin,tiltShift}`, `filter.liquify`, `filter.adaptiveWideAngle`, `filter.cameraRaw`, `filter.vanishingPoint`, `filter.lensCorrection`, `filter.lastFilter`, `filter.convertForSmartFilters`, and the newer Stylize/Pixelate entries). Filters applied to a smart object become re-editable smart filters; **53 PSD filter classes map one-to-one to 53 distinct commands** through `filterFX` (`crates/io/src/smart_map/import.rs` `KNOWN: [Known; 53]`) — Diffuse, Trace Contour, Crystallize, Pointillize and Halftone Pattern round-trip like Photoshop since this release. Field/Iris/Tilt-Shift/Spin Blur are on-canvas; Path Blur is still a parameter dialog.
- **Type**: point/area/paragraph text on canvas, Character/Paragraph controls (paragraph-direction buttons, partial), manual+optical kerning with PSD round trip, variable-font axes, editable font size/leading/tracking preset menus, font menu with favourites/recents/class filters/live preview, `type.warpText`, `type.resolveMissingFonts`; headless editing via `type.editText`, `type.hitTest`, `type.caret`, `type.navigate`. **Type on a path now follows path edits and PSD round-trips through the file's `Txt2`** (`crates/psd/src/tiff.rs`, `docs/gaps.md` G8).
- **Vectors**: Pen/Freeform/Curvature, anchor add/delete/convert, Paths panel, work paths, shape layers with dashed/aligned strokes, corner radii and gradient strokes (gradient stroke turns with the canvas), path↔selection, SVG as editable shape layers or vector smart object (`crates/engine/src/cmd/`, `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; **114 Photoshop event mappings** in the `EVENTS` table (`crates/engine/src/cmd/atn_cmds/events.rs:185`) for `.atn` import/export; unsupported event kinds are preserved verbatim and fail at playback by name; actions ending in Save As/Close work (the result is taken at the Save step).
- **History**: New Snapshot dialog, History Options with Allow Non-Linear History and undoable visibility changes (`crates/engine/src/cmd/`, History panel rebuild).
- **Photomerge**: `file.automate.photomerge` (reposition/perspective) as a cancellable background job.
- **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/cmd/print_cmds.rs`, `docs/printing.md`).
- **Video-lite**: 7 `timeline.*` commands and `file.export.renderVideo` (PNG sequence or animated GIF); animating GIF and WebP open every frame through New Video Layer from File.
- **Update check**: Help › Check for Updates, an opt-out once-per-launch release-feed check, and a **startup self-update prompt modal** (`crates/engine/src/cmd/updates_cmds.rs`, self-update delegated to the shared `craft-update` crate).
### Files — read / write
- **Native `.pcraft`**: directory bundle or zip of STORE entries = `manifest.json` (format version **1**; manifests with too many JSON values are refused before parsing) + content-addressed BLAKE3/zstd `tiles/`+`blobs/` + `thumb.png` + composite preview; incremental, atomic (temp/fsync/rename), crash-recovery autosaves with single-step sidecar replacement (`crates/format/src/`).
- **Read + write**: PSD/PSB/PSDT (PSB chosen by encoded size; spot channels in Photoshop's polarity; `MTrn` merged transparency; layered TIFF text descriptors keep PSD byte order), layered TIFF **including alpha and spot channels as extra samples, plus Photoshop's global layer mask block** (`crates/io/src/tiff_layers.rs`, `crates/codecs/src/codecs/tiff/`), OpenRaster `.ora`, PDF write-only one flattened page, 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, exports the document alpha), OpenEXR, Radiance HDR, FITS, DPX, Cineon, **Amiga IFF (24-bit ILBM writer — read+write since this release)** (`crates/codecs/src/format.rs:107` `Format::ALL [Format; 22]`).
- **22 codec formats**; every one is read+write except the documented asymmetries (`ASYMMETRIC_EXCEPTIONS`, `crates/codecs/src/format.rs`): **read-only** PCX, XPM and DICOM (IFF left that list this release), 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, Krita `.kra`/`.krz`, GIMP `.xcf`/`.xcf.gz`, Affinity `.af*`, SVG; writing an import-only target is refused with a naming error in `export_stages` (`crates/io/src/lib.rs`).
- **Camera raw**: DNG (uncompressed/deflate/lossless-JPEG/lossy-JPEG/**floating-point JPEG XL** — lossy DNGs now decode, falling back to the JPEG XL preview rather than the tiny thumbnail), CR2, TIFF/EP raws (NEF, ARW, PEF), Nikon compressed NEF, Sony cRAW, Panasonic RW2, uncompressed Olympus ORF, uncompressed Fujifilm RAF; **Camera Raw settings sidecars (`.xmp`, `crs:` namespace) load, auto-fill the dialog when beside the raw file, and are written by Save Settings** (`crates/io/src/camera_raw_xmp.rs`); Calibration panel, Auto button and live Camera Raw Defaults. **CR3 remains preview-only — upstream assessed a clean-room CRX reader as not feasible** (`docs/gaps.md` G14).
- **Export As** offers exactly `png, jpg, webp, tif, tga, ico` (`crates/engine/src/cmd/export_preset_cmds.rs:13`); **Save for Web** offers `gif, png8, png24, jpeg, wbmp`; Quick Export as PNG is a single menu item; the Save As picker lists every supported format.
- **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.
- ### Automation surfaces (counts re-measured at v0.18.0; set unchanged from v0.17.0)
+ ### Automation surfaces (counts re-measured at v0.19.0; sets unchanged from v0.18.0 — zero diff files under MCP, control and CLI trees)
- - **One command registry, 631/631 menu items live** (`crates/ui-egui/src/menus/parity.rs`, `docs/parity-checklist.md:5`). Total registry size stays **uncertain by a few dozen** (table macros expand per row; the 16-entry `ADJ` table alone generates 32 commands) — get the authoritative count with `photocraft-cli commands | wc -l`. README's "500+" 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`), 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`. `batch` folder sources now accept every format PhotoCraft opens.
+ - **One command registry, 631/631 menu items live** (`crates/ui-egui/src/menus/parity.rs`, `docs/parity-checklist.md:5`). The live registry at this tag measures **953 commands** (`photocraft-cli commands | wc -l` on the installed bundle that matches this tag; v0.19.0 adds the `smart_svg_cmds` family). Table macros still expand per row (the 16-entry `ADJ` table alone generates 32 commands), so a static count stays uncertain by a few dozen — the CLI remains the authority. README's "500+" and `docs/architecture.md`'s "~700" are floors, not measurements.
+ - **CLI `photocraft-cli` — 8 subcommands + `help`**: `convert, info, run, batch, droplet, commands, mcp, serve` (`apps/photocraft-cli/src/args.rs:31`; re-verified live from the installed bundle), 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`. `batch` folder sources now accept every format PhotoCraft opens.
- **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/dispatch.rs` handler chain). Transports: `apps/photocraft/src/control_server.rs`; 60 s reply timeout, 1 MiB request cap, 16 connections; request paths refuse Windows device names and replacing a non-image file inside the write root. Preferences › Integrations wires Allow Agent Control and the control port in the UI.
- **MCP — 26 tools** (`crates/automation/src/server.rs` `#[tool]` entries): `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` — unchanged as a set since 0.16.0. **2 MCP resources** (`photocraft://document`, `photocraft://commands`, `crates/automation/src/server/conventions.rs`). Bridge-only: the five `ui_*` tools + `control_call`; headless-only: `doc_select`, `doc_close`.
- **Headless server** `photocraft-cli serve` (stdio or `--port`) answers **19 methods** (`crates/automation/src/rpc.rs:42` `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`.
- **Automation budgets**: 8 MiB reply, 5 MiB PNG, preview `max_side` 2048, 64 M source pixels for renders, 256 batch steps, 30 s IO timeout, 10 s auth deadline (`crates/automation/src/budgets.rs`, `security.rs`).
- **Plug-ins**: WebAssembly **ABI v1** only — `plugin.list/install/remove/reload/run` (5 commands), wasmi sandbox, no WASI/filesystem/network/clock, 32 MiB module, 512 MiB memory, 50 M instructions + 4,000 per sample, 60 s per run; duplicate manifest parameters are refused (`docs/plugins.md`, `crates/plugins/src/runtime.rs`).
- **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`. `.pcdroplet` droplets run via `file.automate.runDroplet`.
### Platform
- macOS (universal, signed+notarized), Windows x64/x86/ARM64 (per-user MSI install is now the default policy), Linux x86_64/aarch64 (AppImage, Flatpak, deb, rpm, tarball), FreeBSD 14, 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).
- GPU compositor on wgpu (Metal/Vulkan/DX12/WebGPU) with CPU fallback, `--safe-gpu`, automatic device-loss fallback, one place records why a view left the GPU, and `ui.gpu.simulateLoss` (with `destroy: true`) for testing.
- Pen tablets (`docs/hardware-parity.md`, `crates/tablet`): macOS pressure/tilt/barrel-rotation (rotation not applied to tip angle); Windows **pressure only** (tilt and rotation missing); 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**: `docs/gaps.md` (updated 2026-10-27) is the ranked shortfall list; the ROADMAP percentages were measured at 0.6.0 and have not been recomputed. Performance scorecard: read `docs/scorecard.md` for the current pass/budget rows rather than trusting any snapshot here. The ROADMAP's headline rows (menus, tools, formats) are stale — the generated parity checklist and the code win.
- **PSD fidelity is the blocking gap (G1)**: enforced corpus floors are *minimums* — Photoshop-authored oracles **190/258**, psd-tools 249/308, io corpus 148/169, round trips 258/258-era floors as CI enforces (`crates/io/tests/it/corpus.rs:151-159`). 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 upstream (`docs/gaps.md` G1).
- **No generative AI**: no ONNX runtime or ML crate in the workspace; no Neural Filters, generative fill/expand, sky replacement or generative Remove. Select Subject/Remove Background and Refine Edge are classical algorithms (GrabCut/max-flow segmentation; guided-filter matting — the "local models" are local linear models of the guided filter). Learned segmentation remains open (`docs/gaps.md` G15).
- **No native Photoshop plug-ins**: `.8BF`, CEP and UXP are not hosted; only the Wasm ABI v1 (ABI v2 with panels/progress still open) (`docs/gaps.md` G16).
- **Workspaces that are dialogs (G7)**: Select and Mask is a dialog, 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.
- **Type depth (G8)**: type on a path now follows path edits (closed this release), but there is no RTL visual caret (paragraph-direction UI is partial) and no OpenType Numerals/Stylistic Sets submenus (`docs/gaps.md` G8).
- **Formats absent**: Photoshop PDF **read**; 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`). 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; clean-room CRX assessed as not feasible), "lossy after split" NEF no, no Fuji colour calibration, Olympus ORF and Fujifilm RAF only uncompressed. Raw `.xmp` settings sidecars now load and save (camera-raw settings only — not a general XMP metadata writer).
- **Dead preferences**: 12 of 181 settings do nothing (`docs/scorecard.md:203`; was 20 of 177). 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/tests.rs:315` pins the list; `workspace.rs` enforces it).
- **Localization**: UI strings translated, engine messages stay English; some locales remain partial (`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 — there is a startup prompt now), Windows signing skipped without secrets (DIST-2 partial).
- **Wayland**: no drag-and-drop (winit 0.30 limit) and XWayland fractional scaling is blurred (`README.md`).
- **Docs lag the code** — verify against your installed version before asserting a feature is missing; the generated scorecard/parity-checklist numbers are trustworthy, hand-written prose in the same docs can lag.
## 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`. **Since 0.18.0 the macOS bundle ships `photocraft-cli` beside it** (`/Applications/PhotoCraft.app/Contents/MacOS/photocraft-cli`); older bundles and other platforms: get the 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`). Env equivalents: `PHOTOCRAFT_CONTROL_PORT`, `PHOTOCRAFT_CONTROL_TOKEN(_FILE)`, `PHOTOCRAFT_AUTOMATION_{READ,WRITE}_ROOT`, `PHOTOCRAFT_CONFIG_DIR`, `PHOTOCRAFT_NEW_INSTANCE`. A token flag given **without a value now fails the launch** instead of silently falling back (#6429).
- Without any token flag, 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` `server_token`, `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).
### 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.
### 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).
### 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, `.iff` for Amiga IFF, `.dds` with DDS Options. Long opens/saves run as cancellable background jobs with progress. PSD/PSB open reads channel data as decoded, in bands, without loading the whole file.
- 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.
- 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`). `engine.execute` is *not* gated.
- Keyboard shortcuts: authoritative tables are `crates/engine/src/shell_commands.rs` (shell + tool defaults) and the menu catalogue. 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; Shift+Alt+letter selects a blend mode. Photoshop `.kys` sets import/export through `edit.keyboardShortcuts`.
### State on disk
- Settings: macOS `~/Library/Application Support/Photocraft/` (`preferences.json`, `Presets/` with `.pcbrushes`, `actions.json`, gradient/style presets, `.asl` style libraries, `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`).
- 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.
## 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 generative-AI 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.18.0 (`v0.18.0`, commit `8a87a457`); 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`).
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.18.0`**, commit **`8a87a457abb43ac344deeb3fdb67541b91a72ff1`**, inspection date **2026-11-01**; the v0.17.0 ref was fetched for set diffs (registry shape unchanged; locale catalogs 21 → 26; CLI now bundled in the macOS app). The installed app is still 0.17.0.
- - Installed app: `/Applications/PhotoCraft.app` — version 0.17.0, bundle id `ai.storyteller.photocraft`, minimum macOS 11.0, `Contents/MacOS/PhotoCraft` the only binary (no CLI) (`Contents/Info.plist`); the app was not launched.
+ - Repo: https://github.com/storytold/photocraft — inspected at tag **`v0.19.0`**, commit **`2df9f807702a935184041355a86405380bda8274`**, inspection date **2026-11-02**; the v0.18.0 ref was fetched for diffs (MCP/control/CLI zero diff files; locale catalogs 26 = 26; `smart_svg_cmds` joins the engine registry). The installed app matches the tag at 0.19.0.
+ - Installed app: `/Applications/PhotoCraft.app` — version 0.19.0, bundle id `ai.storyteller.photocraft`, minimum macOS 11.0; `Contents/MacOS` holds `PhotoCraft` and the bundled `photocraft-cli`, which was run read-only (`--version`, `commands`, `--help`); the GUI was not launched.
- Key files read: `README.md`, `docs/gaps.md`, `docs/scorecard.md`, `docs/parity-checklist.md`, `docs/{control-protocol,mcp,file-format-parity,plugins,printing,batch-naming}.md`, `Cargo.toml`, `apps/photocraft/src/launch_args.rs`, `apps/photocraft-cli/src/args.rs`, `crates/engine/src/{commands,cmd,cmd/gallery_cmds,cmd/atn_cmds/events,cmd/cutout_cmds,cmd/updates_cmds,cmd/export_preset_cmds,filters,filters_ext}.rs`, `crates/ui-egui/src/state/tool.rs`, `crates/ui-egui/src/menus/parity.rs`, `crates/ui-egui/src/control/dispatch.rs`, `crates/doc/src/effects.rs`, `crates/color/src/blend.rs`, `crates/algo/src/{artistic,matting}.rs`, `crates/codecs/src/format.rs`, `crates/codecs/src/codecs/tiff/`, `crates/codecs/src/codecs/iff.rs`, `crates/io/src/{lib,camera_raw_xmp,tiff_layers}.rs`, `crates/io/src/smart_map/import.rs`, `crates/io/tests/it/corpus.rs`, `crates/format/src/manifest.rs`, `crates/raw/src/lib.rs`, `crates/automation/src/{server,server/conventions,rpc,security,budgets}.rs`, `crates/automation/src/workspace{,/tests.rs}`, `crates/plugins/src/runtime.rs`.
- - Shared libraries: `storytold/craft-libs`, all craft crates pinned to one revision in `Cargo.toml` (`craft-ui`, `craft-print`, `craft-docpath`, `craft-jpeg`, `craft-update` at rev `92d392cf…`); only the pinning relationship was consulted, not shared-library behaviour.
+ - Shared libraries: `storytold/craft-libs`, all craft crates pinned to one revision in `Cargo.toml` (`craft-ui`, `craft-print`, `craft-docpath`, `craft-jpeg`, `craft-update`); 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.