lightcraft · diff
git:20261023.8287b5e to git:20261026.76415fe
98 added, 89 removed. Audit A to A.
---
name: lightcraft
description: >-
LightCraft is a native photo library and non-destructive raw developer — an open-source, clean-room
reimplementation of Adobe Lightroom Classic — written in pure Rust (macOS app at
/Applications/LightCraft.app, plus a separate `lightcraft-cli` and a WebAssembly web build). Trigger
- this skill whenever the user mentions LightCraft, `.lcpreset`, a LightCraft library folder
+ this skill whenever the user mentions LightCraft, `.lcpreset`, `.lcprint`, a LightCraft library folder
(`catalog.snap` / `catalog.log`), `lightcraft-cli`, or LightCraft's control/MCP server; asks what it
can import, develop or export (RAW: DNG/CR2/CR3/ARW/NEF/RAF/RW2/ORF/PEF/SRW/MRW/SR2; export:
JPEG/PNG/TIFF/WebP/AVIF/JPEG XL/PSD/DNG/original); asks about its menus or keyboard shortcuts; wants
to automate, script, batch-render or agent-drive it (CLI `run`/`render`/`snapshot`/`merge`/`calibrate`/
`denoise-serve`, JSON-lines control channel on 127.0.0.1, MCP tools, `print.*`/`slideshow.*`
commands); wants to prepare inputs, migrate a Lightroom Classic catalog, repair a library folder, fit
camera colour profiles from raws, or use a lensfun lens database; wants exports, XMP sidecars or camera
colour verified; or troubleshoots it. Trigger even when the app is not named but the job is
photo-library or raw-development work — "cull this shoot", "develop this RAW", "apply a preset and
export JPEGs", "import my Lightroom catalog", "why won't this CR3 open" — and prefer this skill over
generic Lightroom advice, because LightCraft's formats and gaps differ from Adobe's in ways that matter.
---
- **Audit basis:** this checkout is tag `v0.12.0`, commit `479a05a0be4756be460859c4d42b4fbb7a60e99d`. The installed `/Applications/LightCraft.app` reports version `0.12.0`, its `Info.plist` records build commit `479a05a0be4756be460859c4d42b4fbb7a60e99d`, and its binary prints `lightcraft 0.12.0` — the bundle matches this source exactly. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since.
+ **Audit basis:** this checkout is tag `v0.14.0`, commit `03c88c166c054c009db2a47187d59991f6232b85`. The installed `/Applications/LightCraft.app` reports version `0.14.0`, its `/Applications/LightCraft.app/Contents/Info.plist` `LightCraftBuildCommit` is the same SHA, and its binary prints `lightcraft 0.14.0` — the bundle matches this source. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since. There is no `references/` directory; deep detail lives in `evidence.md` beside this file and in the repo's own `docs/`.
## Overview
- LightCraft is a photo **library** plus a non-destructive **raw developer** — an open-source, clean-room reimplementation of Adobe Lightroom Classic 15.5 / Lightroom 9.6 (`ROADMAP.md:5`, `docs/target-app-parity.md:3`), written from scratch in pure Rust with no Adobe assets (`README.md:21`, `AGENTS.md`). Its surfaces are the desktop app (`apps/lightcraft`, eframe + wgpu + native macOS menus), `lightcraft-cli` (headless render, batch commands, MCP server, headless UI snapshots), and a WebAssembly build of the same UI (`apps/lightcraft-web`).
+ LightCraft is a photo **library** plus a non-destructive **raw developer** — an open-source, clean-room reimplementation of Adobe Lightroom Classic 15.5 / Lightroom 9.6 (`ROADMAP.md:3`, `docs/target-app-parity.md`), written from scratch in pure Rust with no Adobe assets (`README.md`, `AGENTS.md`). Its surfaces are the desktop app (`apps/lightcraft`, eframe + wgpu + native macOS menus), `lightcraft-cli` (headless render, batch commands, MCP server, headless UI snapshots), and a WebAssembly build of the same UI (`apps/lightcraft-web`).
- **Maturity: alpha.** The project's generated checklist summary reports **83.7% of 542 in-scope rows** (P0 98.3%, P1 95.0%, P2 56.5%), while `ROADMAP.md` and `docs/target-app-parity.md` still print **81.6% of 532** — they lag the generated checklist. Its own readiness estimate is **~61%** overall, **~51%** for a mainstream working photographer and **~65%** for an essentials user; beta is an estimated ~340–600 agent-hours away (`ROADMAP.md:15-22`). The gap is quality and coverage — camera colour, render fidelity, some raw variants, AI models — not missing buttons. Source: GitHub `storytold/lightcraft`, MIT OR Apache-2.0 (`Cargo.toml:6-9`).
+ **Maturity: alpha.** At this tag the generated parity summary reports **83.5% of 543 in-scope rows** (P0 98.3%, P1 94.4%, P2 56.7%, `docs/parity-checklist.md:58`), while `ROADMAP.md:15` and `docs/target-app-parity.md:15` still print **81.6% of 532** — they lag the generated block. The project's readiness estimate is unchanged at **~61%** overall, **~51%** for a mainstream working photographer and **~65%** for an essentials user; beta is an estimated ~340–600 agent-hours away (`ROADMAP.md:3-22`). The gap is quality and coverage — camera colour, render fidelity, some raw variants, AI models — not missing buttons. Source: GitHub `storytold/lightcraft`, MIT OR Apache-2.0 (`Cargo.toml:6-9`).
## Capabilities (verified)
### Library, catalog and views — implemented
- - **The library is a folder, not a document.** `catalog.snap` (JSON snapshot) + `catalog.log` (append-only JSON-lines op log: `seq`, CRC-32, op; fsynced; a torn final record is repaired; a damaged mid-log is preserved as `catalog.log.damaged-<seq>`), plus `presets.json`, `view.json`, `prefs.json`, `thumbs/` and `Originals/` for copy-imported photos (`crates/engine/src/library.rs:1-24`; `crates/catalog/src/journal.rs:1-30`). Default: `LightCraft Library` under the user's `Pictures` folder; `LIGHTCRAFT_LIBRARY` overrides (`crates/engine/src/library.rs:33,63-68`).
- - **Crash-safe and single-writer.** Every top-level command that changes the catalog persists its ops (fsynced) before replying; a failed write replies `"saved in memory but not written to disk: …; LightCraft will retry"`, keeps the change in memory and retries. `ui.inspect` exposes `unsaved`; an unopenable library is reported as `libraryProblem` (`crates/engine/src/library.rs:10-20`; `crates/ui-egui/src/control.rs:135-136`). One process per library: `catalog.lock` refuses a second writer with "This library is already open in …" (`crates/catalog/src/lock.rs:84-88`).
- - **Library model.** Albums in folders, smart albums (rule trees incl. File Path rules), stacks/auto-stack, virtual copies, versions and history, ratings/flags/colour labels, filter bar, fielded search (including people named on faces), sort, Photo Grid / Square Grid / People, filmstrip, Compare and Survey culling with synced zoom, **Reference view** (⇧R, pin a photo with `photo.setReference`), **Local** folder browsing without import (edits go to XMP sidecars; "Add to My Photos" adopts the photo), Recently Deleted, missing-file relink, and **Assisted Culling** (classical focus score, clipping, burst grouping; no expression/eye detection) (`crates/engine/src/cmd/{organize,library,filters,query,browse,cull}.rs`; `crates/ui-egui/src/menus.rs:71-279`; `docs/parity-checklist.md` rows `LRC-LIB-REFVIEW`, `LR-LIB-CULL`).
- - **Workspace.** Detail view with Edit / Info / Keywords / History panels, full-screen preview (`F`), Lights Out (`Shift+L`), second window (`⌘F11`), Before/After (`Y`, `Shift+Y`, `Alt+Y`), soft proofing (`S`; partial — see limitations), info overlay (`⌘I`) and View Options (`⌘J`), a loupe layout-image overlay, and dockable/splittable panels via `ui.dock` / `ui.dock.reset` (`crates/ui-egui/src/menus.rs`; `docs/control-protocol.md` "Methods").
- - **Long-running tasks** (imports, exports, merges, downloads, face scans, preview builds) live in one activity stack, visible to agents as `activity.list` / `activity.cancel`; quitting asks first (`docs/background-tasks.md:1-10`; `crates/engine/src/cmd/activity.rs`).
- - **Faces and focus points.** XMP face names, editable face boxes and the People view work with no model; detection (YuNet) and recognition (SFace/AuraFace) are opt-in model installs run by LightCraft's own pure-Rust CPU interpreter (`docs/faces.md:1-24`). `photo.inspect` carries `meta.focus` for supported bodies; View ▸ Show Focus Point (⌥F), zoom-to-focus, and a background `photo.readFocusPoints` sweep — currently Sony α1 and A7S III only (`docs/focus-points.md:1-40`).
+ - **The library is a folder, not a document.** `catalog.snap` (JSON snapshot) + `catalog.log` (append-only JSON-lines op log: `seq`, CRC-32, fsynced; a torn final record is repaired; a mid-log is preserved as `catalog.log.damaged-<seq>`), plus `presets.json`, `view.json`, `prefs.json`, `thumbs/` and `Originals/` for copy-imported photos (`crates/engine/src/library.rs:1-22`; `crates/catalog/src/journal.rs:1-40`). Default: `LightCraft Library` under the user's `Pictures` folder; `LIGHTCRAFT_LIBRARY` overrides (`crates/engine/src/library.rs:63-70`).
+ - **Crash-safe and single-writer.** Every top-level command that changes the catalog persists its ops (fsynced) before replying; a failed write keeps the change in memory, reports `unsaved` and retries. One process per library: `catalog.lock` refuses a second writer with "This library is already open in …" (`crates/catalog/src/lock.rs:86`; `crates/ui-egui/src/control.rs` `ui.inspect`).
+ - **Library model.** Albums in folders, smart albums (rule trees incl. File Path rules; "in the last…" date rules refresh when the session clock crosses a minute), stacks/auto-stack, virtual copies, versions and history, ratings/flags/colour labels + configurable label sets, filter bar with **Enable Filters** (`⌘L`, `library.toggleFilters` sets a filter aside and brings it back), fielded search (including people named on faces), sort, Photo Grid / Square Grid / People, filmstrip, Compare and Survey culling with synced zoom, **Reference view** (⇧R, `photo.setReference`), **Local** folder browsing without import (edits go to XMP sidecars; "Add to My Photos" adopts the photo), Recently Deleted, missing-file relink (matches a library made on Windows by file name), **hold view** (`library.holdView`) and **Assisted Culling** (classical focus score, clipping, burst grouping; no expression/eye detection) (`crates/engine/src/cmd/organize.rs`, `crates/engine/src/cmd/library.rs`, `crates/engine/src/cmd/filters.rs`, `crates/engine/src/cmd/query.rs`, `crates/engine/src/cmd/browse.rs`, `crates/engine/src/cmd/cull.rs`; `crates/ui-egui/src/menus.rs:71-290`; `docs/parity-checklist.md` rows `LRC-LIB-REFVIEW`, `LR-LIB-CULL`).
+ - **Choosing a library at launch.** Settings ▸ General ▸ **At launch**: *Open the last library* or *Ask me*; with *Ask me* (or Alt/Option held as the window appears) a window lists recent libraries with name, folder, last-opened time and a background existence check. `app.recentLibraries` returns `{path, name, exists, checking, open, lastOpened}` and `{remove: path}`/`{clear: true}`; `app.newLibrary` makes and opens one (`crates/ui-egui/src/panels/settings.rs:1481-1519`; `crates/ui-egui/src/menus.rs:259-262`; `docs/control-protocol.md` "Choosing the library at launch").
+ - **Workspace.** Detail view with Edit / Info / Keywords / History panels, full-screen preview (`F`), Lights Out (`⇧L`), second window (`⌘F11`), Before/After (`Y`, `⇧Y`, `⌥Y`), soft proofing (`S`; partial — see limitations), info overlay (`⌘I`) and View Options (`⌘J`), a loupe layout-image overlay, multi-select Info panel (`view.infoSelectedPhotos` shows the values the selection shares and marks mixed fields), and dockable/splittable panels via `ui.dock` / `ui.dock.reset` (`crates/ui-egui/src/menus.rs`; `docs/control-protocol.md` "Methods").
+ - **Long-running tasks** (imports, exports, merges, downloads, preview builds, the face scan) live in one activity stack, visible to agents as `activity.list` / `activity.cancel`; quitting asks first (`crates/engine/src/cmd/activity.rs`; `docs/background-tasks.md:1-10`; `ui.inspect` → `quitPrompt`).
+ - **Previews and proxies.** `library.buildPreviews|cancelPreviews|previewProgress` build the disk thumbnail cache; `photo.smartPreview` plus `library.smartPreviews|smartPreviewsLocation` build/relocate smart previews that keep working when originals are offline (`crates/engine/src/cmd/previews.rs`; `docs/camera-preview-colour.md` "smart previews").
+ - **Faces and focus points.** XMP face names, editable face boxes and the People view work with no model; detection (YuNet) and recognition (SFace/AuraFace) are opt-in model installs run by LightCraft's own pure-Rust CPU interpreter (`crates/engine/src/cmd/face_models.rs`, `crates/engine/src/cmd/face_detect.rs`, `crates/engine/src/cmd/face_recognize.rs`; `docs/faces.md:1-24`). `photo.inspect` carries `meta.focus`; View ▸ Focus Point (⌥F), zoom-to-focus and a background `photo.readFocusPoints` sweep — only Sony `ILCE-1` (α1) and `ILCE-7SM3` (A7S III) at this tag (`docs/focus-points.md:36,66-67`; `crates/engine/src/focus_scan.rs`).
### Import and raw decoding — implemented, with coverage limits
- - **Importable extensions are one list of 38** in `crates/engine/src/import.rs` (`EXTENSIONS`): `jpg jpeg png tif tiff webp dng cr2 cr3 nef nrw arw sr2 srf raf orf rw2 rwl raw pef srw psd jxl gif bmp heic heif avif iiq crw mrw x3f kdc mos erf 3fr fff mef`. Support is decided by **extension** (`is_supported`, `crates/engine/src/import.rs:373`), then the decoder may still refuse. Modes: **Add** (reference in place), **Copy** (into `Originals/` or a destination, with date/month/flat/folder-template filing and rename templates), **Move** (copy, verify, then remove the source and its sidecars); duplicates are detected by path and by content; copy imports can convert raws to DNG (`crates/engine/src/import.rs:47-56,167-197`; `crates/engine/src/cmd/library.rs:1527`).
- - **Raw decoded from sensor data** (`README.md` RAW row; `docs/raw-parity.md:140-185`; `crates/raw/src/lib.rs:117,425`): DNG (uncompressed, LJ92, lossy/Smart Preview, LinearRaw, Deflate, DNG 1.7 JPEG XL tiles, Apple ProRAW, row/column interleave), CR2, CR3 (lossless CRX Bayer + version 0x100/0x200 C-RAW; R5 Mark II), ARW (uncompressed, cRAW/ARW2, lossless L/M/S, A900 packed, **ARW6 Compressed/HQ on the ILCE-7RM6**), NEF (uncompressed, lossless/lossy, lossy-after-split, and **table-less 34713 strips stored as packed 12/14-bit**), RAF (uncompressed and lossless/lossy compressed, Bayer + X-Trans), RW2/RWL/Panasonic RAW (formats 2/4/5/6/7/8 incl. tiled high-resolution format 8), PEF, ORF (uncompressed incl. 12.8 bpp), SRW (uncompressed), ERF, MEF, **Minolta MRW**, 3FR/FFF (DNG-like layouts), Kodak DCS520C TIFF, and Sony SR2 (DSC-R1).
- - **Per-model coverage is measured, not implied.** Of Adobe's 1,446 listed models: **551 verified** from sensor data (38%), 656 decoder-path-unverified, 202 preview-only, 37 unsupported (`docs/raw/coverage.tsv`; headline text in `docs/raw-parity.md:20-27` still says 201/38). Use-weighted: **~94% of photographers' cameras fully decode, ~85% on a verified body** (`docs/raw-parity.md:25-27`).
- - **Anything it cannot decode falls back to the embedded camera JPEG** and is flagged `previewOnly`; the app rechecks those photos in the background on version change, and headless callers use `library.recheckPreviewOnly` (`docs/mcp.md` "Preview-only raws after an update"). Sparse header + embedded-JPEG reads avoid pulling sensor data over a network share (56.5 MB → 5.9 MB on the test share; `docs/raw-preview-reads.md:1-13`).
- - **HEIC/HEIF decode** exists behind the `heif` build feature; it is off by default for HEVC-patent reasons and on in official packaging (`crates/codecs/Cargo.toml:43-46`; `packaging/macos/package.sh:65`). The installed 0.12.0 binary contains `lightcraft_heif` / `heic_rs` symbols, so the official build decodes HEIC — a source build without the feature does not.
- - **New at v0.12.0 (re-verified in code):** Minolta MRW (`crates/raw/src/vendor/mrw.rs:1-10,68`), table-less packed NEF (`crates/raw/src/vendor/nef.rs:10-11,348-367,485-512`), RW2 tiled format 8 (`crates/raw/src/vendor/rw2.rs:53`), Sony SR2/SRF discovery (`crates/engine/src/import.rs:37-44`; SR2 decodes, F828 SRF is preview-only), plus the Fujifilm nine-knot lens-table fix and Nikon Z5II Kelvin presets (`crates/raw/src/vendor/raf_lens.rs`; `crates/raw/src/kelvin_builtin.rs:23-28`).
- - **Lightroom Classic catalog import** (`library.inspectLightroom` / `library.importLightroom`, File ▸ Import Lightroom Catalog…): reads `.lrcat` directly (read-only pure-Rust SQLite incl. committed WAL; 16 GiB / 16,777,216-page cap), one-way and approximate, imports ratings/flags/labels/keywords/collections/virtual copies/mapped develop settings (`docs/lightroom-catalog-import.md:1-40`). Also `folder.scanChanges` / `folder.synchronize` to import new files, relink moved ones, move missing ones to Recently Deleted and read newer sidecars; and `library.autoImport` / `library.autoImportScan` for a watched folder (`crates/engine/src/cmd/{sync,library}.rs`).
+ - **Importable extensions are one list of 39** in `crates/engine/src/import.rs:37-44` (`EXTENSIONS`): `jpg jpeg png tif tiff webp dng cr2 cr3 nef nrw arw sr2 srf raf orf rw2 rwl raw pef srw psd psb jxl gif bmp heic heif avif iiq crw mrw x3f kdc mos erf 3fr fff mef`. Support is decided by **extension** (`is_supported`, `crates/engine/src/import.rs:373`), then the decoder may still refuse. Modes: **Add** (reference in place), **Copy** (into `Originals/` or a destination, with date/month/flat/folder-template filing and rename templates), **Move** (copy, verify, then remove the source and its sidecars); duplicates are detected by path and by content; copy imports can convert raws to DNG (`crates/engine/src/import.rs:47-56`; `crates/engine/src/cmd/library.rs`).
+ - **Raw decoded from sensor data** (`docs/raw-parity.md`; `crates/raw/src/lib.rs`): DNG (uncompressed, LJ92, lossy/Smart Preview, LinearRaw, Deflate, DNG 1.7 JPEG XL tiles, Apple ProRAW, row/column interleave), CR2, CR3 (lossless CRX Bayer + version 0x100/0x200 C-RAW; R5 Mark II), ARW (uncompressed, cRAW/ARW2, lossless L/M/S, A900 packed, ARW6 Compressed/HQ on the ILCE-7RM6), NEF (uncompressed, lossless/lossy, lossy-after-split, table-less 34713 strips as packed 12/14-bit), RAF (uncompressed and lossless/lossy compressed, Bayer + X-Trans), RW2/RWL/Panasonic RAW (formats 2/4/5/6/7/8 incl. tiled high-resolution format 8), PEF, ORF (uncompressed incl. 12.8 bpp), SRW (uncompressed), ERF, MEF, Minolta MRW, 3FR/FFF, Kodak DCS520C TIFF, and Sony SR2.
+ - **Per-model coverage is measured, not implied.** Of Adobe's 1,446 listed models: **551 verified** from sensor data (38%), 656 decoder-path-unverified, **202 preview-only**, **37 unsupported** (`docs/raw/coverage.tsv`, sums re-checked at this tag; the headline table in `docs/raw-parity.md:20-21` still says 201/38). Use-weighted: **~94% of photographers' cameras fully decode, ~85% on a verified body** (`docs/raw-parity.md:23-24`).
+ - **Anything it cannot decode falls back to the embedded camera JPEG** and is flagged `previewOnly`; the app rechecks those photos in the background on version change, and headless callers use `library.recheckPreviewOnly` (`crates/engine/src/cmd/recheck.rs`; `docs/mcp.md`). Sparse header + embedded-JPEG reads avoid pulling sensor data over a network share (56.5 MB → 5.9 MB on the test share; `docs/raw-preview-reads.md:1-13`).
+ - **HEIC/HEIF decode** exists behind the `heif` build feature; it is off by default for HEVC-patent reasons and **on in official packaging** (`crates/codecs/Cargo.toml:43-46` still claims releases don't ship it — `packaging/macos/package.sh:65` passes `--features lightcraft/heif`, and the installed 0.14.0 binary contains `lightcraft_heif::decode|probe|decode_thumbnail`). AVIF is import-listed but never decodes (export only).
+ - **Lightroom Classic catalog import** (`library.inspectLightroom` / `library.importLightroom`, File ▸ Import Lightroom Catalog…): reads `.lrcat` directly (read-only pure-Rust SQLite incl. committed WAL; 16 GiB / 16,777,216-page cap), one-way and approximate, imports ratings/flags/labels/keywords/collections/virtual copies/mapped develop settings. At this tag the app first asks whether the catalog **merges into the open library or goes into a new library** — `file.importLightroom {into?: "open"|"new", name?, location?}` makes that choice headlessly, and Settings ▸ General can turn the question off (`crates/ui-egui/src/lightroom_destination.rs:1-30`; `docs/lightroom-catalog-import.md:87`). Also `folder.scanChanges` / `folder.synchronize` and `library.autoImport` / `library.autoImportScan` (`crates/engine/src/cmd/sync.rs`, `crates/engine/src/cmd/library.rs`).
### Develop, masks and merge — implemented
- - **122 named develop sliders** in one spec table plus **10 indexed specs** (8 Point Color + 2 Red Eye), addressed as `pointColor.<i>.<control>` / `redEye.<control>`; 16 sections: Light, Curve, Color, Mixer, BwMix, Grading, Effects, Vignette, Grain, Detail, Optics, Geometry, Profile, Calibration, PointColor, RedEye (`crates/develop/src/controls.rs:14-31,170,296`; count it with `lightcraft-cli controls --json`).
- - The pipeline is scene-referred linear Rec.2020 32-bit float with OkLCh-based colour and a filmic shoulder for raw, pass-through for untouched JPEGs (`README.md:83-110`). **Process versions V1 (frozen) and V2 (current)** make re-renders deterministic; `develop.updateProcess` moves photos forward (`docs/process-versions.md`; `crates/develop/src/settings.rs`).
+ - **122 named develop sliders** in one spec table plus **10 indexed specs** (8 Point Color + 2 Red Eye), addressed as `pointColor.<i>.<control>` / `redEye.<control>`; 16 sections: Light, Curve, Color, Mixer, BwMix, Grading, Effects, Vignette, Grain, Detail, Optics, Geometry, Profile, Calibration, PointColor, RedEye (`crates/develop/src/controls.rs:144-296`; count it with `lightcraft-cli controls --json`).
+ - **22 built-in develop profiles** (looks, `crates/engine/src/presets.rs:476`) — Color, Neutral, Vivid, Landscape, Portrait, Monochrome plus film/cinema/muted/B&W families — a profile *browser* with recent profiles; the profile is separate from the raw camera profile below.
+ - The pipeline is scene-referred linear Rec.2020 32-bit float with OkLCh-based colour and a filmic shoulder for raw, pass-through for untouched JPEGs (`README.md`). **Process versions V1 (frozen) and V2 (current)** make re-renders deterministic; `develop.updateProcess` moves photos forward (`docs/process-versions.md`; `crates/develop/src/settings.rs`).
- `develop.set` / `develop.adjust` / `develop.auto` / `develop.wb` / `develop.wbPick` / `develop.matchExposure` / `develop.copy|paste|sync|autoSync` / `develop.merge` / `develop.reset*` / `develop.beginInteraction|endInteraction`; white balance is Kelvin on raws with colour matrices or measured presets, relative (6500 = as shot) elsewhere (`crates/engine/src/cmd/develop.rs`; `docs/control-protocol.md` "White balance").
- - **Masking**: brush, linear/radial gradients, luminance range, colour range, Subject / Sky / Background heuristics, plus Object and Describe via an optional SAM 3 model; Add/Subtract/Intersect, invert, and **17 local sliders + a colour overlay + Amount** per mask. Straight agent brush strokes: `mask.brushStroke {connect: true}` (`crates/develop/src/settings.rs:829-852,926-995`; `crates/engine/src/cmd/masks.rs:301-378`; `docs/ai-masks.md`).
- - **Crop, geometry and repair**: crop/straighten/rotate/flip with aspect ratios that stay portrait after rotation, auto-straighten, Upright Auto/Level/Vertical/Full/Guided, Constrain Crop (tightens the actual crop); Heal/Clone spots with auto source, non-generative Fill (PatchMatch), editable pins, Visualize Spots, Red Eye and Pet Eye (`crates/engine/src/cmd/develop.rs`; `crates/inpaint/src/lib.rs:1-22`).
- - **Photo Merge**: HDR (auto-align, deghost incl. moving lights, deterministic order, bracket batching), Panorama (spherical/cylindrical/perspective, boundary warp, auto crop; hard maxima 512 MP / 65,000 px; default output at the photos' own resolution unless `maxMegapixels` caps it), HDR Panorama → DNG; commands `merge.hdr|panorama|hdrPanorama|hdrBatch`; CLI `merge hdr|panorama|hdr-panorama|hdr-batch` (`docs/merge.md:1-60`; `crates/merge/src/pano/mod.rs:44-57`; `crates/engine/src/cmd/merge.rs`).
- - **GPU**: the develop pipeline as wgpu compute kernels on Metal/Vulkan/DX12, checked against the CPU pipeline to within 1/255; CPU fallback on device limits or errors (`README.md:326,383`; `docs/gpu-pipeline.md`). **AI Denoise** is a per-photo switch, Bayer RAW only, with **no weights bundled** (user-installed ONNX + `denoise-model.json`), results cached under `<library>/denoise/`; `lightcraft-cli denoise-serve` serves tiles to other machines over a LAN/VPN with token auth (`docs/denoise.md:1-40`; `apps/lightcraft-cli/src/main.rs` USAGE).
+ - **Masking**: brush, linear/radial gradients, luminance range, colour range, Subject / Sky / Background heuristics, plus Object and Describe via an optional SAM 3 model; Add/Subtract/Intersect, invert, and **17 local sliders + a colour overlay + Amount** per mask. Straight agent brush strokes: `mask.brushStroke {connect: true}` (`crates/develop/src/settings.rs:829-852`; `crates/engine/src/cmd/masks.rs`; `docs/ai-masks.md`).
+ - **Crop, geometry and repair**: crop/straighten/rotate/flip with aspect ratios that stay portrait after rotation, **aspect lock** (`crop.lockAspect`, `A`), auto-straighten (refuses turns > 10° and needs ≥ 3 agreeing lines), Upright Auto/Level/Vertical/Full/Guided, Constrain Crop; Heal/Clone spots with auto source, non-generative Fill (PatchMatch), editable pins, Visualize Spots, Red Eye and Pet Eye (`crates/engine/src/cmd/develop.rs`; `crates/inpaint/src/lib.rs:1-22`).
+ - **Photo Merge**: HDR (auto-align, deghost incl. moving lights, deterministic order, bracket batching), Panorama (spherical/cylindrical/perspective, boundary warp, auto crop, Lanczos-3 resampling; hard maxima 512 MP / 65,000 px; default output at the photos' own resolution unless `maxMegapixels` caps it), HDR Panorama → DNG; commands `merge.hdr|panorama|hdrPanorama|hdrBatch`; CLI `merge hdr|panorama|hdr-panorama|hdr-batch` (`docs/merge.md:20-31`; `crates/merge/src/pano/mod.rs:34-57`; `crates/merge/src/flow.rs`, `crates/merge/src/refine.rs`).
+ - **GPU**: the develop pipeline as wgpu compute kernels on Metal/Vulkan/DX12, checked against the CPU pipeline to within 1/255; CPU fallback on device limits or errors (`README.md`; `docs/gpu-pipeline.md`). **AI Denoise** is a per-photo switch, Bayer RAW only, with **no weights bundled** (user-installed ONNX + `denoise-model.json`), results cached under `<library>/denoise/`; `lightcraft-cli denoise-serve` serves tiles to other machines over a LAN/VPN with token auth (`docs/denoise.md:1-40`; `apps/lightcraft-cli/src/main.rs` USAGE).
### Presets, colour and lens data — implemented
- - **41 built-in presets** with an Amount slider 0–200%; copy/paste/sync of chosen settings groups; `.lcpreset` is LightCraft's own preset file. Import also accepts XMP presets, legacy `.lrtemplate`, Luminar `.lmp`/`.mplumpack`, `.zip` bundles, `.cube` LUT profiles and local-adjustment effect presets (`.lrtemplate`, `.xmp`, `.lceffect`) (`crates/engine/src/presets.rs:14,158,215`; `crates/engine/src/cmd/{preset_files,lut_profiles,mask_effect_files}.rs`).
- - **Camera colour** starts from DNG matrices, guarded per-file fits to the camera JPEG, **8 bundled profiles** (Sony ILCA-99M2, ILCE-7CR, ILCE-7M4; Fujifilm X-H2S, X-T4; Nikon Z 6II, Z 8, D5300 — `crates/engine/src/camera_profiles.rs:25-36`; `assets/camera-profiles/`), 52 spectral matrices (`crates/raw/src/spectral.rs:240`), or a neutral fallback. `lightcraft-cli calibrate` fits per-camera profiles from a user's own raws (Sony ARW, Nikon NEF, Fujifilm RAF; `apps/lightcraft-cli/src/main.rs` USAGE). Measured ΔE against the camera JPEG is 2.9–10 (`docs/gaps.md:18`).
- - **Lens corrections**: embedded DNG opcodes, Panasonic/Leica RW2/RWL distortion, Nikon Z 6II / Z 8 / Z f distortion, Sony ILCE-7RM4A / ILCE-7CR / ILCE-7RM6 distortion, and **Fujifilm distortion + vignetting for 59 validated bodies**; all go through Optics ▸ Enable Lens Corrections (`crates/raw/src/vendor/{arw_lens,arw_lens_rm6,nef_lens,raf_lens,rw2}.rs`; `docs/{sony,sony-ilce7rm6,nikon,fuji}-lens-corrections.md`). For everything else, a **user-supplied lensfun XML database** via `LIGHTCRAFT_LENSFUN_DIR` adds distortion/TCA/vignetting; no database is bundled, Adobe `.lcp` is never used, matching is automatic, and fisheye is not corrected (`crates/lensdb/src/lib.rs:1-14`; `crates/engine/src/lens_profiles.rs:15`; `docs/lens-profiles.md`).
+ - **41 built-in presets** with an Amount slider 0–200%; copy/paste/sync of chosen settings groups; `.lcpreset` is LightCraft's own preset file. Import also accepts XMP presets, legacy `.lrtemplate`, Luminar `.lmp`/`.mplumpack`, `.zip` bundles, `.cube` LUT profiles and local-adjustment effect presets (`.lrtemplate`, `.xmp`, `.lceffect`) (`crates/engine/src/presets.rs:215`; `crates/engine/src/cmd/preset_files.rs`, `crates/engine/src/cmd/lut_profiles.rs`, `crates/engine/src/cmd/mask_effect_files.rs`).
+ - **Camera colour** starts from DNG matrices, guarded per-file fits to the camera JPEG, **8 bundled profiles** (Sony ILCA-99M2, ILCE-7CR, ILCE-7M4; Fujifilm X-H2S, X-T4; Nikon Z 6II, Z 8, D5300 — `crates/engine/src/camera_profiles.rs:27-36`; `assets/camera-profiles/`), **52 spectral matrices** (`crates/raw/src/spectral_table.rs`, 52 `Camera` entries), or a neutral fallback. `lightcraft-cli calibrate` fits per-camera profiles from a user's own raws (Sony ARW, Nikon NEF, Fujifilm RAF; `apps/lightcraft-cli/src/main.rs` USAGE). Measured ΔE against the camera JPEG is 2.9–10 (`docs/gaps.md:18`).
+ - **Lens corrections**: embedded DNG opcodes, Panasonic/Leica RW2/RWL distortion, Nikon Z 6II / Z 8 / Z f distortion, Sony ILCE-7RM4A / ILCE-7CR / ILCE-7RM6 distortion, and **Fujifilm distortion + vignetting for 59 validated bodies**; all go through Optics ▸ Enable Lens Corrections (`crates/raw/src/vendor/arw_lens.rs`, `crates/raw/src/vendor/arw_lens_rm6.rs`, `crates/raw/src/vendor/nef_lens.rs`, `crates/raw/src/vendor/raf_lens.rs`, `crates/raw/src/vendor/rw2.rs`; `docs/sony-lens-corrections.md`, `docs/sony-ilce7rm6-lens-corrections.md`, `docs/nikon-lens-corrections.md`, `docs/fuji-lens-corrections.md`). For everything else, a **user-supplied lensfun XML database** via `LIGHTCRAFT_LENSFUN_DIR` adds distortion/TCA/vignetting; no database is bundled, Adobe `.lcp` is never used, matching is automatic, and fisheye is not corrected (`crates/lensdb/src/lib.rs:1-14`; `crates/engine/src/lens_profiles.rs`; `docs/lens-profiles.md`).
### Export and output modules — implemented
- - **10 export formats** (`ExportFormat`, `crates/engine/src/export.rs:16-33`): `jpeg png tiff psd webp avif jxl original dng rendereddng`. `psd` is a **flat** PSD (8/16-bit, no layers); `original` copies the file plus an XMP sidecar carrying the edits; `dng` writes a raw photo as DNG with edits embedded (uncompressed, LJ92, Deflate or lossy JPEG tiles); `rendereddng` bakes the render into a linear 16-bit float DNG.
- - **Validated export options** (`ExportOptions`, `crates/engine/src/export.rs:619-717`; keys at `:719-754`): `quality 1-100`, `bitDepth 8|10|16|32` per format, `colorSpace srgb|displayP3|adobeRgb|proPhoto|rec2020` with embedded ICC, `metadata all|allExceptCamera|copyright|none`, `removeLocation`, `sharpen none|screen|matte|glossy` + amount, `tiffCompression none|lzw|zip|deflate`, `dngCompression lossless|deflate|uncompressed|lossy`, `conflict unique|overwrite|skip`, resize by `longEdge|shortEdge|width|height|megapixels|percent` with `dontEnlarge`, `fit dimensions|widthHeight`, `ppi`, `limitKb` (JPEG), text/image `watermark` (vertical, rotation, nine anchors), `hdr` with `hdrTransfer pq|hlg` (gain-map JPEG, PQ/HLG AVIF, PQ PNG/JPEG XL, float TIFF), `jxlLossless|jxlEffort|jxlProgressive`, `destinationMode: "sameAsOriginal"`, and macOS-only `addToPhotos` + `photosAlbum`.
- - **File-naming and folder templates** share one token set — `{name} {num} {seq} {seq:N} {date} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext}`; unknown tokens stay as typed, illegal characters become `-`, collisions get a suffix (`crates/engine/src/rename.rs:1-20`; `photo.renameTokens` lists them).
- - **Contact Sheet PDF**: A4/Letter, rows/columns, captions, 150 dpi sRGB; limits 1,000 photos / 100 pages / 256 MiB; atomic replace; command `export.contactSheet` (`crates/engine/src/contact_sheet.rs:49-51,87-154`).
- - **Print module** as `print.*` commands (`print.get/set/reset`, `print.cell.*`, `print.layout.*`, `print.exportPdf`, `print.template.*`): Single Image, Contact Sheet, Picture Package (Auto Layout), Custom Package; zoom-to-fill / rotate-to-fit; borders/strokes; A3–A5, Letter/Legal/Tabloid, 4×6/5×7/8×10/13×19 or custom; margins; guides/rulers; saved templates; printer/paper ICC + intent + black-point compensation; 8/16-bit colour-managed PDF; Page Options (page numbers, page info, crop marks) on Single Image and Contact Sheet (`crates/engine/src/cmd/print.rs:321-351`; `crates/engine/src/print/layout.rs:38-50`; `crates/engine/src/print_pdf.rs:1-29`).
- - **Slideshow**: playback (fades, random order, repeat, prepared previews), layouts, overlays (identity plate, rating stars, text tokens, export watermark), intro/ending title screens, built-in + user templates, and background export to PDF (page per slide), numbered JPEG sequence, or **AV1 video in MP4/WebM (no audio, at most 2 hours)**; commands `slideshow.*`, progress via `activity.list`, `slideshow.exportStatus` (`crates/engine/src/cmd/slideshow.rs:375+`; `crates/engine/src/slideshow_export.rs:9-11,39`; `docs/slideshow.md`).
- - **Apple Photos (macOS only)**: `app.export {addToPhotos: true, photosAlbum}` or `export.addToPhotos {paths}`; the first run triggers a macOS Automation permission prompt (`docs/apple-photos.md`; `crates/engine/src/export.rs`).
+ - **10 export formats** (`ExportFormat`, `crates/engine/src/export.rs:16-33`): `jpeg png tiff psd webp avif jxl original dng rendereddng`. `psd` is a **flat** PSD (8/16-bit, no layers); `original` copies the file plus an XMP sidecar carrying the edits; `dng` writes a raw photo as DNG with edits embedded; `rendereddng` bakes the render into a linear 16-bit float DNG.
+ - **Validated export options** (`ExportOptions`, `crates/engine/src/export.rs:627-687`, parsed by `from_params` at `:870`): `quality 1-100`, `bitDepth 8|10|16|32` per format, `colorSpace srgb|displayP3|adobeRgb|proPhoto|rec2020` with embedded ICC, `metadata all|allExceptCamera|copyright|none`, `removeLocation` (an `original` export is refused when a PSD/PSB embeds GPS it cannot strip), `sharpen none|screen|matte|glossy` + amount, `tiffCompression`, `dngCompression`, `conflict unique|overwrite|skip`, resize by `longEdge|shortEdge|width|height|megapixels|percent` with `dontEnlarge`, `fit dimensions|widthHeight`, `ppi`, `limitKb` (JPEG, incl. HDR gain-map JPEG), text/image `watermark` (vertical, rotation, nine anchors, image orientation honoured), `hdr` with `hdrTransfer pq|hlg`, `jxlLossless|jxlEffort|jxlProgressive`, `destinationMode: "sameAsOriginal"`, and macOS-only `addToPhotos` + `photosAlbum`.
+ - **File-naming and folder templates** share one token set — `{name} {num} {seq} {seq:N} {date} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext}`; unknown tokens stay as typed, illegal characters become `-`, collisions get a suffix (`crates/engine/src/rename.rs`; `photo.renameTokens` lists them).
+ - **Contact Sheet PDF**: A4/Letter, rows/columns, captions, 150 dpi sRGB; limits 1,000 photos / 100 pages / 256 MiB; atomic replace; command `export.contactSheet` (`crates/engine/src/contact_sheet.rs:87-92`).
+ - **Print module** as `print.*` commands (`print.get/set/reset`, `print.cell.*`, `print.layout.*`, `print.exportPdf`, `print.template.list|save|apply|delete|export|import`): Single Image, Contact Sheet, Picture Package (Auto Layout), Custom Package; zoom-to-fill / rotate-to-fit; borders/strokes; A3–A5, Letter/Legal/Tabloid, 4×6/5×7/8×10/13×19, Japanese photo/postcard sizes or custom; margins; guides/rulers; saved templates **as `.lcprint` files**; printer/paper ICC + intent + black-point compensation; 8/16-bit colour-managed PDF; Page Options (page numbers, page info, crop marks) on Single Image and Contact Sheet (`crates/engine/src/cmd/print.rs:385-401`; `crates/engine/src/print/template.rs:34` `FILE_EXT = "lcprint"`; `crates/engine/src/print_pdf.rs`).
+ - **Slideshow**: playback (fades, random order, repeat, prepared previews), layouts, overlays (identity plate, rating stars, text tokens, export watermark), intro/ending title screens, built-in + user templates with import/export, and background export to PDF (page per slide), numbered JPEG sequence, or **AV1 video in MP4/WebM (no audio, at most 2 hours)**; commands `slideshow.*`, progress via `activity.list`, `slideshow.exportStatus` (`crates/engine/src/cmd/slideshow.rs`; `crates/engine/src/slideshow_export.rs:40-42`; `docs/slideshow.md`).
+ - **Apple Photos (macOS only)**: `app.export {addToPhotos: true, photosAlbum}` or `export.addToPhotos {paths}`; the first run triggers a macOS Automation permission prompt (`docs/apple-photos.md`).
### Interchange — implemented
- - **XMP sidecars** read/write, manual (`⌘S`, `photo.saveMetadataToFile`) or automatic (`library.xmpPreferences {autoWrite, naming: stem|full}`); LightCraft's fields plus the full develop state as `lc:settings` under `http://ns.lightcraft.app/lc/1.0/`; owned properties are replaced, capture time/GPS only when LightCraft has a value, **foreign namespaces kept byte for byte**, an existing sidecar is merged into (never replaced), writes are atomic, failed writes retry and `library.retrySidecars` forces them (`docs/xmp-interop.md`; `crates/engine/src/cmd/xmp.rs:85-112`).
- - Reading Adobe `crs:` develop settings is **approximate** and reports per-photo warnings; Lightroom radial masks apply inside the circle as Lightroom does (`docs/xmp-interop.md`; `docs/whats-new.md` 2026-10-14).
- - GPX track logs, offline reverse geocoding (GeoNames towns ≥15k; `metadata.reverseGeocode {maxDistance}`), keyword list import/export, XMP face names (face boxes are catalog-only and never written to XMP), and `library.importLightroom` (`crates/engine/src/cmd/{geocode,keywords}.rs`; `docs/file-format-parity.md`; `crates/catalog/src/model.rs:198`).
- - macOS edit-in: **Edit in PhotoCraft / Edit in Other Editor** renders `<name>-Edit.tif` (16-bit Adobe RGB by default; flat `.psd` or 8-bit optional), stacks it on the original and opens it; at most 50 photos per call (`crates/engine/src/cmd/convert.rs:123-156,398`).
+ - **XMP sidecars** read/write, manual (`⌘S`, `photo.saveMetadataToFile`) or automatic (`library.xmpPreferences {autoWrite, naming: stem|full}`); LightCraft's fields plus the full develop state as `lc:settings` under `http://ns.lightcraft.app/lc/1.0/` (`crates/meta/src/xmp.rs:18`); owned properties are replaced, capture time/GPS only when LightCraft has a value, **foreign namespaces kept byte for byte** (including when they use `lc:`/`xmp:` prefixes), an existing sidecar is merged into, writes are atomic, failed writes retry and `library.retrySidecars` forces them (`docs/xmp-interop.md`; `crates/engine/src/cmd/xmp.rs`).
+ - Reading Adobe `crs:` develop settings is **approximate** and reports per-photo warnings; Lightroom radial masks apply inside the circle as Lightroom does (`docs/xmp-interop.md`).
+ - **PSD/PSB metadata**: imports embedded EXIF (resource 1058) and XMP (resource 1060) image resources, so capture dates, GPS and descriptive fields survive; PSD edit copies keep their capture date (`crates/meta/src/psd.rs:1-20`; `docs/gaps.md` "Closed recently" 2026-10-22).
+ - GPX track logs, offline reverse geocoding (GeoNames towns ≥15k; `metadata.reverseGeocode {maxDistance}`), keyword lists with keyword sets (nine keywords per set, `⌥1–⌥9`; next/previous set `⌥0`/`⌥⇧0`), XMP face names (face boxes are catalog-only and never written to XMP), and capture-time edits (`photo.setCaptureTime`) (`crates/engine/src/cmd/geocode.rs`, `crates/engine/src/cmd/keywords.rs`, `crates/engine/src/cmd/manage.rs`; `docs/file-format-parity.md`).
+ - macOS edit-in: **Edit in PhotoCraft / Edit in Other Editor** renders `<name>-Edit.tif` (16-bit Adobe RGB by default; flat `.psd` or 8-bit optional), stacks it on the original and opens it; at most 50 photos per call (`crates/engine/src/cmd/convert.rs`).
### Automation surface — implemented (the strongest part of the app)
- - **Everything is a command.** At this tag: **370 engine command specs** (counted from `cmd!` entries across `crates/engine/src/cmd/*.rs`, wired by 37 `specs()` sources in `crates/engine/src/cmd/mod.rs:144-180`) and **186 UI commands** in `UI_COMMANDS` plus **18 language commands** (204 UI entries reported by the running app's `engine.commands`). `engine.commands` returns id, label, menu path, shortcut, parameter doc and current enablement; UI, CLI, control channel and MCP all dispatch by id.
- - **Counts and deltas from the previous (v0.11.0) skill:** engine commands 370 → **370** (unchanged); UI commands 183 → **186** (added `view.rightPanel`, `view.viewOptions` (⌘J), `app.showInFolders`); language commands 14 → **18** (added `fi`, `id`, `sv`, `vi`; 204 UI entries reported in total); import extensions 36 → **38** (added `sr2`, `srf`); export formats **10** (unchanged); develop controls **122 + 10 indexed** (the old "122 + 8" counted Point Color only and missed the 2 Red Eye specs); built-in presets **41** (unchanged); bundled camera profiles **8** (unchanged; docs still say 5); interface languages 14 → **18**; CLI subcommands **10** (unchanged). MCP helper tools are **28 headless / 39 in connect mode** (unchanged), plus one generated `cmd_*` tool per reported command: **370** headless, **574** when connected (370 engine + 204 UI/language); `tools/list` therefore totals **398** tools headless and **613** connected. `--compact` lists helpers only.
- - **Control channel**: `lightcraft --control 7980` (or `LIGHTCRAFT_CONTROL_PORT`) serves JSON-lines on `127.0.0.1:PORT`, **loopback only, no authentication**, at most 16 connections. Requests are answered on the UI thread with a 60 s timeout; a request that has not started and draws no frame for 5 s is failed and never retried; an idle connection closes after 5 minutes; a malformed line closes the connection (`apps/lightcraft/src/control_server.rs:24,67-77`; `docs/control-protocol.md:1-40`). Methods cover `engine.execute` (alias `ui.menu.invoke`), `engine.commands`, `ui.menu.list/tree`, `ui.inspect`, `ui.widgets`, clicks/drags/hover, raw pointer, image-coordinate `ui.pointer`, keys/text/clipboard/scroll/pinch, `ui.set`, dialogs, `ui.resize`, `ui.zoomFactor`, headless `ui.screenshot`, `ui.render`, activity, slideshow control and `app.quit`.
- - **MCP server**: `lightcraft-cli mcp [--demo | --library DIR | FILES…]` headless, or `lightcraft-cli mcp --connect 127.0.0.1:7980` to drive the live app. Hand-written JSON-RPC over stdio, protocol `2025-06-18` (older revisions accepted), 7 resources (`lightcraft://document`, `//commands`, `//library`, `//photos`, `//photo/active`, `//develop/active`, `//controls`). Helpers: `command_list/run/batch`, `doc_inspect`, `render_preview`, `import`, `query_photos`, `select_photos`, `list_controls`, `get_develop`, `set_develop`, `apply_preset`, `crop`, `render_photo`, `export` (plus the legacy `list_commands` / `run_command` aliases); connect mode adds `ui_inspect`, `ui_screenshot`, `screenshot`, `inspect_ui`, `set_ui`, `list_widgets`, `click`, `press_key`, `type_text`, `clipboard`, `pointer_gesture`. `--compact` limits `tools/list` to helpers (`crates/mcp/src/tools.rs:78-338,345,415`; `crates/mcp/src/server.rs:16-41`; `docs/mcp.md`).
- - **CLI subcommands** (`apps/lightcraft-cli/src/main.rs:28-115` USAGE + dispatch): `run`, `mcp`, `render`, `snapshot`, `merge`, `synth-merge`, `denoise-serve`, `commands`, `controls`, `calibrate`. `run` takes `--demo|--library DIR|--connect [ADDR]`, repeatable `--import`, `--script FILE|-`, `--keep-going`; `render` takes `--set control=value`, `--settings look.json|.lcpreset|.xmp`, `--preset ID`, `--size`, `--quality`, and any export option via `--opt KEY=VALUE`; `merge` writes `<first>-HDR.dng` / `-Pano.dng` / `-HDR-Pano.dng` next to the first file (`--preview OUT.png` writes only a preview).
- - **Headless UI snapshots** need no window and no GPU: `lightcraft-cli snapshot --demo --script tour.jsonl -o out.png --size 1600x1000 --scale 2` runs the full egui UI on the CPU. In a running app use `ui.screenshot {"path": …, "headless": true}`; windowed capture falls back to headless after 2 s. In snapshot scripts `ui.settle {timeoutMs}` waits for renders (`docs/control-protocol.md` "Headless rendering"; `apps/lightcraft-cli/src/main.rs` USAGE).
+ - **Everything is a command.** At this tag: **375 engine command specs** (counted from the `cmd!` literals in `crates/engine/src/cmd/*.rs`, wired by 37 `specs()` sources in `crates/engine/src/cmd/mod.rs:140-180`) and **190 UI commands** in `UI_COMMANDS` (`crates/ui-egui/src/menus.rs:71`) plus **18 language commands** (`:16`), i.e. **208 UI entries**, so a running app's `engine.commands` reports **583** commands. `engine.commands` returns id, label, menu path, shortcut, parameter doc and current enablement; UI, CLI, control channel and MCP all dispatch by id.
+ - **Counts and deltas from the previous (v0.12.0) skill:** engine commands 370 → **375** (added `keyword.nextSet`, `keyword.previousSet`, `library.toggleFilters`, `print.template.export`, `print.template.import`; none removed); UI commands 186 → **190** (added `crop.lockAspect` (A), `panel.addKeywords` (⌘K), `view.frameRate` (Help ▸ Debug), `view.infoSelectedPhotos`); language commands 18 → **18**; import extensions 38 → **39** (added `psb`); export formats **10** (unchanged); develop controls **122 + 10 indexed** (unchanged); built-in presets **41** (unchanged); develop profiles **22** (the previous skill omitted them — counted now); bundled camera profiles **8** (unchanged; `docs/gaps.md:18` and `docs/raw-parity.md` still say 5); interface languages **18** (unchanged); CLI subcommands **10** (unchanged). **Correction:** the previous skill's "28 helper tools headless / 39 connected" was wrong — it counted the whole 28-entry helper list as headless and then added the 11 UI-only tools again; measured at both tags it is **17 headless / 28 connected** (`crates/mcp/src/tools.rs:78-345`).
+ - **Control channel**: `lightcraft --control 7980` (or `LIGHTCRAFT_CONTROL_PORT`) serves JSON-lines on `127.0.0.1:PORT`, **loopback only, no authentication**, at most 16 connections, 4 MiB line cap. Requests are answered on the UI thread with a 60 s timeout; a request that has not started and draws no frame for 5 s is failed and never retried; an idle connection closes after 5 minutes; a malformed line closes the connection (`apps/lightcraft/src/control_server.rs:24-77`; `docs/control-protocol.md:1-40`). **24 dispatch arms exposing 29 callable method names**, method list at `crates/ui-egui/src/control.rs` (`handle`, `:196`): `engine.execute` (aliases `ui.menu.invoke`, `command`), `engine.commands`, `ui.menu.list/tree`, `ui.inspect`, `ui.widgets`, `ui.clickWidget|dragWidget|hoverWidget`, raw `ui.move|click|drag`, image-coordinate `ui.pointer`, `ui.key`, `ui.text`, `ui.clipboard`, `ui.scroll`, `ui.zoom` (pinch), `ui.set`, `ui.dialog.confirm|cancel`, `ui.resize`, `ui.zoomFactor`, `ui.screenshot`, `ui.render`, `ui.dropFiles` (**new since v0.12.0**: drag-and-drop imports, with `hover` for the drop card), `app.quit`.
+ - **MCP server**: `lightcraft-cli mcp [--demo | --library DIR | FILES…]` headless, or `lightcraft-cli mcp --connect 127.0.0.1:7980` to drive the live app. Hand-written JSON-RPC over stdio, protocol `2025-06-18` (older revisions accepted), 7 resources (`lightcraft://document`, `//commands`, `//library`, `//photos`, `//photo/active`, `//develop/active`, `//controls`). Helpers: `command_list/run/batch`, `doc_inspect`, `render_preview`, `import`, `query_photos`, `select_photos`, `list_controls`, `get_develop`, `set_develop`, `apply_preset`, `crop`, `render_photo`, `export` (plus the documented legacy `list_commands` / `run_command`); connect mode adds `ui_inspect`, `ui_screenshot`, `screenshot`, `inspect_ui`, `set_ui`, `list_widgets`, `click`, `press_key`, `type_text`, `clipboard`, `pointer_gesture`. One generated `cmd_*` tool per reported command, so `tools/list` totals **392** tools headless (17 helpers + 375 engine commands) and **611** connected (28 helpers + 583 commands); `--compact` lists helpers only (`crates/mcp/src/tools.rs:78-345,415`; `crates/mcp/src/server.rs:16-41,200`; `docs/mcp.md`).
+ - **CLI subcommands** (`apps/lightcraft-cli/src/main.rs:28-114` USAGE + dispatch at `:142-178`): `run`, `mcp`, `render`, `snapshot`, `merge`, `synth-merge`, `denoise-serve`, `commands`, `controls`, `calibrate`. `run` takes `--demo|--library DIR|--connect [ADDR]`, repeatable `--import`, `--script FILE|-`, `--keep-going`; `render` takes `--set control=value`, `--settings look.json|.lcpreset|.xmp`, `--preset ID`, `--size`, `--quality`, and any export option via `--opt KEY=VALUE`; `merge` writes `<first>-HDR.dng` / `-Pano.dng` / `-HDR-Pano.dng` next to the first file (`--preview OUT.png` writes only a preview); `commands` now lists ids sorted for readable families.
+ - **Headless UI snapshots** need no window and no GPU: `lightcraft-cli snapshot --demo --script tour.jsonl -o out.png --size 1600x1000 --scale 2` runs the full egui UI on the CPU. In a running app use `ui.screenshot {"path": …, "headless": true}`; windowed capture falls back to headless after 2 s. In snapshot scripts `ui.settle {timeoutMs}` waits for renders (`docs/control-protocol.md` "Headless rendering").
+ - **Web build automation**: the browser build exposes `await globalThis.lightcraft.command("<id>", "<json params>")` returning JSON text; params must be a JSON object (a typo such as `{ids: [3]}` is refused instead of running the command on the selection) (`apps/lightcraft-web/index.html:39-40`; `apps/lightcraft-web/src/web.rs:635`; `apps/lightcraft-web/src/automation.rs:1-20`).
### Platform — implemented
- - Native **macOS / Windows / Linux** (deb, rpm, AppImage, Flatpak, tarball; riscv64 tarball), a **FreeBSD** tarball, and a **WebAssembly build of the same UI** (WebGL2 via eframe's glow backend) with a persistent browser library in OPFS/IndexedDB; WebGPU and Safari/Firefox testing are in progress; JXL export is native-only and merges have no file access in the browser (`docs/web.md:1-30,179-180`; `README.md` Downloads).
- - **18 interface languages** in code: English, Simplified/Traditional Chinese, Japanese, Brazilian Portuguese, Spanish, German, Russian, French, Ukrainian, Polish, Czech, Italian, Georgian, Swedish, Finnish, Indonesian, Vietnamese (`crates/ui-egui/src/i18n.rs:118-137`). Coverage is partial and lower-layer errors stay English; no RTL or complex-script shaping.
- - Display colour management: macOS screen profile, Linux colord, or a chosen ICC file via Settings ▸ Display / `app.displayProfile`; Windows WCS is not implemented (`docs/display-profiles.md`; `docs/parity-checklist.md` `LR-PREF-DISPLAY`). Portable mode (`portable.txt` beside the executable) and isolated automation profiles via `LIGHTCRAFT_CONFIG_DIR` (`apps/lightcraft/src/main.rs:766-790`; `docs/control-protocol.md` "Isolated automation profiles").
+ - Native **macOS / Windows / Linux** (deb, rpm, AppImage, Flatpak, tarball; riscv64 tarball), a **FreeBSD** tarball, and a **WebAssembly build of the same UI** (WebGL2 via eframe's glow backend) with a persistent browser library in OPFS/IndexedDB; WebGPU and Safari/Firefox testing are in progress; JXL export is native-only and merges have no file access in the browser (`docs/web.md`; `README.md` Downloads).
+ - **18 interface languages** in code: English, Simplified/Traditional Chinese, Japanese, Brazilian Portuguese, Spanish, German, Russian, French, Ukrainian, Polish, Czech, Italian, Georgian, Swedish, Finnish, Indonesian, Vietnamese (`crates/ui-egui/src/i18n.rs:22-46`; `crates/ui-egui/src/menus.rs:16-37`). Coverage is partial and lower-layer errors stay English; no RTL or complex-script shaping.
+ - Display colour management: macOS screen profile, Linux colord, or a chosen ICC file via Settings ▸ Display / `app.displayProfile`; Windows WCS is not implemented (`docs/display-profiles.md`; `docs/parity-checklist.md` `LR-PREF-DISPLAY`). Portable mode (`portable.txt` beside the executable) and isolated automation profiles via `LIGHTCRAFT_CONFIG_DIR` (`apps/lightcraft/src/main.rs:793-815`; `docs/control-protocol.md` "Isolated automation profiles").
+ - **Tests in CI**: Linux x64 runs `cargo xtask ci` on every push/PR (`.github/workflows/ci.yml`), FreeBSD runs a nightly build/package check (`.github/workflows/freebsd.yml`); macOS/Windows package but do not run the suite (`docs/gaps.md:26`).
## Known limitations and not-implemented
Read this before assigning work. Each item is the project's own admission or a code path that is absent.
**Raw and colour**
- - **Camera colour is estimated, not measured.** Raws start from a per-file fit to the camera JPEG, the **8** bundled profiles, 52 spectral matrices, or a neutral fallback; measured ΔE against the camera JPEG is 2.9–10 (`docs/gaps.md:18`; `docs/raw-parity.md:24-25`). Note the doc bug: `docs/gaps.md` and `docs/raw-parity.md` still say "5 bundled profiles" while `assets/camera-profiles/` and `camera_profiles.rs` have 8.
+ - **Camera colour is estimated, not measured.** Raws start from a per-file fit to the camera JPEG, the **8** bundled profiles, 52 spectral matrices, or a neutral fallback; measured ΔE against the camera JPEG is 2.9–10 (`docs/gaps.md:18`). Doc bug: `docs/gaps.md` and `docs/raw-parity.md` still say "5 bundled profiles" while `assets/camera-profiles/` and `crates/engine/src/camera_profiles.rs` have 8.
- **No bundled lens database.** Only in-file data plus the opt-in lensfun XML database; Adobe's 3,635 profiles are never used — deliberately. lensfun matching is automatic (no manual picker), raws only, no fisheye, and not validated against real photos (`docs/gaps.md:25`; `docs/lens-profiles.md:40-45`).
- - **Preview-only raws**: compressed Olympus/OM ORF (66 of 78 models), Nikon High-Efficiency (HE / HE★) NEF, several CRX variants (vertical-tile C-RAW, partial subband, multi-tile 0x200, adaptive low-frequency gains), compressed Samsung SRW, and the long tail IIQ / Hasselblad-compressed / X3F / CRW / GPR / DCR (`docs/gaps.md:19,23-24,37`; `docs/raw-parity.md:140-185`). All 110 public CC0 CR3s decode; 26 CR3 bodies are verified, but some variants have no public sample (`docs/gaps.md:2`).
+ - **Preview-only raws**: compressed Olympus/OM ORF (66 of 78 models), Nikon High-Efficiency (HE / HE★) NEF, several CRX variants (vertical-tile C-RAW, partial subband, multi-tile 0x200, adaptive low-frequency gains), compressed Samsung SRW, and the long tail IIQ / Hasselblad-compressed / X3F / CRW / GPR / DCR (`docs/gaps.md:19,23-24,37`; `docs/raw-parity.md`). All 110 public CC0 CR3s decode; 26 CR3 bodies are verified, but some variants have no public sample (`docs/cr3.md`).
- **AVIF cannot be imported** (no permissive pure-Rust AV1 decoder); AVIF is export-only. `.HIF` HEIC stills are not CR3; HEIC 4:2:2/4:4:4 frames are refused (`crates/engine/src/import.rs:37-44`; `docs/file-format-parity.md`).
- - **PSD import is the merged composite only** — no layers or layer styles; LightCraft writes flat PSDs. `.psb` is *not* importable even though `crates/codecs/src/psd.rs` parses it (`psb` is absent from `EXTENSIONS`).
+ - **PSD/PSB import is the merged composite only** — no layers or layer styles; LightCraft writes flat PSDs (`crates/codecs/src/psd.rs:1-5`; `crates/codecs/src/sniff.rs:21`).
- **Render fidelity is unmeasured**: tone, highlights, texture/clarity/dehaze, noise reduction and sharpening are tuned by eye, so edits imported from Lightroom will not look identical (`docs/gaps.md:20`).
**AI and enhancement**
- - **AI masks**: Subject, Sky, Background (and the model's People/Landscape kinds) are classical heuristics, not models; only **Object** and **Describe** use SAM 3, and the 3.4 GB model is **not bundled**. The built-in download-mirror list is empty, so the in-app download needs a user-configured `LIGHTCRAFT_SAM3_MIRRORS` or a manually installed model folder; every other mask works without it (`docs/ai-masks.md:1-40,108-132`; `docs/gaps.md:22`).
- - **AI Denoise ships no weights**: user-installed ONNX + `denoise-model.json`; Bayer RAW only, no X-Trans, and it does not run on merged linear DNGs. The optional RawNIND weights are GPL-3.0 and gated behind a release policy (`docs/denoise.md:14-24`; `docs/gaps.md:45`).
+ - **AI masks**: Subject, Sky, Background (and the model's People/Landscape kinds) are classical heuristics, not models; only **Object** and **Describe** use SAM 3, and the 3.4 GB model is **not bundled**. The built-in download-mirror list is empty, so the in-app download needs a user-configured `LIGHTCRAFT_SAM3_MIRRORS` or a manually installed model folder (`docs/ai-masks.md`; `docs/gaps.md:22`).
+ - **AI Denoise ships no weights**: user-installed ONNX + `denoise-model.json`; Bayer RAW only, no X-Trans, and it does not run on merged linear DNGs. The optional RawNIND weights are GPL-3.0 and gated behind a release policy (`docs/denoise.md:14-24`; `docs/gaps.md:28`).
- **Absent: Super Resolution, Raw Details, content-aware/generative remove, reflections and distraction removal** (`docs/gaps.md:28-29`).
**Output and interchange**
- - **Print** lacks identity plate / watermark / photo-info overlays, Cut Guides, Auto Layout for *Custom* Package, printer-driver Page Setup, direct printer output and JPEG output; it produces a colour-managed PDF (`docs/gaps.md:32`).
- - **HDR display (EDR / Windows HDR) is not implemented**: HDR editing, the headroom limit and SDR rendition work, but you edit blind on an HDR monitor (`docs/gaps.md:31`). Export supports gain-map JPEG, PQ/HLG AVIF, PQ PNG/JPEG XL and float TIFF.
- - **Slideshow** has playback, layouts, overlays, titles, templates and PDF/JPEG/AV1 export, but no music and no dedicated module editor (`README.md` slideshow row; `docs/gaps.md:16`).
- - **Lightroom catalog migration is one-way and approximate**; unsupported Adobe profiles/AI settings are archived, and main-file pages are read on demand up to 16 GiB (`docs/lightroom-catalog-import.md`; `docs/gaps.md:13`).
- - **No custom ICC output profile**, and **Render to DNG** exists only as the export format `rendereddng`, not as a catalog command (`docs/file-format-parity.md`; `docs/gaps.md:25`).
+ - **Print** lacks identity plate / watermark / photo-info overlays (beyond Page Options), Cut Guides, Auto Layout for *Custom* Package, printer-driver Page Setup, direct printer output and JPEG output; it produces a colour-managed PDF (`docs/gaps.md:32`).
+ - **HDR display (EDR / Windows HDR) is not implemented**: HDR editing, the headroom limit and SDR rendition work, but you edit blind on an HDR monitor (`docs/gaps.md:31`).
+ - **Slideshow** has playback, layouts, overlays, titles, templates and PDF/JPEG/AV1 export, but no music and no dedicated module editor (`README.md` slideshow row; `docs/gaps.md:33`).
+ - **Lightroom catalog migration is one-way and approximate**; unsupported Adobe profiles/AI settings are archived (`docs/lightroom-catalog-import.md`; `docs/gaps.md:30`).
+ - **No custom ICC output profile**, and **Render to DNG** exists only as the export format `rendereddng`, not as a catalog command (`docs/file-format-parity.md`; `docs/gaps.md:42`).
**Do not assign**
- - Video (playback, trim, edits, export): a `Video` media kind exists in the catalog model, but the importer's extension list is stills-only and there is no playback or editing path (`crates/catalog/src/model.rs:46`; `crates/engine/src/import.rs:37-44`; `docs/gaps.md:18`).
- - Map view, Book module, publish services, tethered capture, Lightroom plug-ins / SDK; no HDR display (`docs/gaps.md:14-19`).
- - **No document file format.** There is no "save as" for an edited photo: the library folder is the unit of persistence and edits live in the catalog and/or an XMP sidecar. The macOS bundle declares **no** document types at this release (`/Applications/LightCraft.app/Contents/Info.plist` has no `CFBundleDocumentTypes`; `packaging/macos/Info.plist.in`; `crates/engine/src/library.rs:1-24`).
+ - Video (playback, trim, edits, export): a `Video` media kind exists in the catalog model, but the importer's extension list is stills-only and there is no playback or editing path (`crates/catalog/src/model.rs`; `crates/engine/src/import.rs:37-44`; `docs/gaps.md:35`).
+ - Map view, Book module, publish services, tethered capture, Lightroom plug-ins / SDK; no HDR display (map/book `docs/gaps.md:33`, tethered `:34`, plug-ins `:36`, HDR display `:31`).
+ - **No document file format.** There is no "save as" for an edited photo: the library folder is the unit of persistence and edits live in the catalog and/or an XMP sidecar. The macOS bundle declares **no** `CFBundleDocumentTypes` (`/Applications/LightCraft.app/Contents/Info.plist`; `packaging/macos/Info.plist.in`; `crates/engine/src/library.rs:1-22`).
**Platform, quality and docs**
- - Per-model verification is thin: 551 of 1,446 Adobe models verified (38%), ~85% of photographers on a verified body (`docs/gaps.md:10`). Tests in CI run on FreeBSD only; macOS/Windows/Linux package but do not run the suite (`docs/gaps.md:9`). Windows/Linux GPU paths are only partly proven (`docs/gaps.md:27`).
- - Localization is partial; lower-layer errors stay English; no RTL or complex-script shaping (`docs/localization-parity.md`). Accessibility is partial: AccessKit tree is audited, but brush/red-eye keyboard access and a real screen-reader session remain (`docs/gaps.md:23`).
- - **Docs that disagree with code (trust the code):** 5 vs 8 bundled camera profiles; `docs/raw-parity.md` headline 201 preview / 38 unsupported vs generated `docs/raw/coverage.tsv` 202 / 37; ROADMAP 81.6%/532 vs checklist summary 83.7%/542; `docs/file-format-parity.md` still marks PSD write ⬜ and "no HLG" while export implements both; `docs/localization-parity.md` says "Other languages LightCraft ships (4)" then lists 6 and calls Polish/Swedish missing although both ship in `i18n.rs`.
+ - Per-model verification is thin: 551 of 1,446 Adobe models verified (38%), ~85% of photographers on a verified body (`docs/gaps.md:27`). macOS/Windows/Linux package but do not run the test suite. Windows/Linux GPU paths are only partly proven (`docs/gaps.md:44`).
+ - Localization is partial; lower-layer errors stay English; no RTL or complex-script shaping (`docs/localization-parity.md`). Accessibility is partial: AccessKit tree audited, but brush/red-eye keyboard access and a real screen-reader session remain (`docs/gaps.md:39`).
+ - **Docs that disagree with code (trust the code):** 5 vs 8 bundled camera profiles; `docs/raw-parity.md` headline 201 preview / 38 unsupported vs the generated `docs/raw/coverage.tsv` 202 / 37; ROADMAP 81.6%/532 vs checklist summary 83.5%/543; `docs/file-format-parity.md` still marks PSD write ⬜ while export implements it; `crates/codecs/Cargo.toml:45` says releases don't ship the `heif` feature while `packaging/macos/package.sh:65` passes it and the installed binary decodes HEIC; `docs/gaps.md:38` names Vietnamese as absent although it ships in `crates/ui-egui/src/i18n.rs`.
## How to operate
- **Launch (macOS).** `open -a /Applications/LightCraft.app` for the GUI, or the binary directly when you need flags: `/Applications/LightCraft.app/Contents/MacOS/LightCraft`. Real flags, parsed in `apps/lightcraft/src/main.rs:766-790`:
+ **Launch (macOS).** `open -a /Applications/LightCraft.app` for the GUI, or the binary directly when you need flags: `/Applications/LightCraft.app/Contents/MacOS/LightCraft`. Real flags, parsed in `apps/lightcraft/src/main.rs` (HELP at `:793`):
```
lightcraft [OPTIONS] [FILES/FOLDERS…] # positional paths are imported at launch
- --library DIR open (or create) this library (default: Pictures/LightCraft Library; env LIGHTCRAFT_LIBRARY)
+ --library DIR open (or create) this library (env LIGHTCRAFT_LIBRARY; default: last library opened,
+ else ~/Pictures/LightCraft Library; Settings ▸ General ▸ At launch: Ask me shows a list)
--no-demo don't seed a new library with the procedural demo photos
--memory throwaway in-memory session (alias --demo); writes nothing
--control PORT serve the JSON-lines control channel on 127.0.0.1:PORT
--version, --help
```
- Useful environment: `LIGHTCRAFT_LIBRARY`, `LIGHTCRAFT_CONTROL_PORT`, `LIGHTCRAFT_CONFIG_DIR` (isolated settings/logs/model profile), `LIGHTCRAFT_LANGUAGE=de|ja|zh-hans|…`, `LIGHTCRAFT_GPU_BACKEND=dx12|vulkan|metal|auto|off`, `LIGHTCRAFT_GPU=0`, `LIGHTCRAFT_LOG=info|debug` / `RUST_LOG`, `LIGHTCRAFT_MEMORY_MB`, `LIGHTCRAFT_NO_PREFS`, `LIGHTCRAFT_NO_NATIVE_MENU`, `LIGHTCRAFT_SAM3_DIR|_MIRRORS|_TOKEN|_REMOTE`, `LIGHTCRAFT_LENSFUN_DIR`, `LIGHTCRAFT_CAMERA_PROFILES`, `LIGHTCRAFT_DENOISE_MODELS|_URL|_TOKEN`, `LIGHTCRAFT_HDR_DISPLAY=0`, `LIGHTCRAFT_PROFILE=1` (`apps/lightcraft/src/main.rs`; `crates/segment/src/remote.rs`; `crates/engine/src/{lens_profiles,camera_profiles,memory}.rs`; `docs/`). Logs go to the settings folder's `logs/lightcraft.log`, rotated on each start, 16 MiB cap (`README.md` logs section).
+ Useful environment: `LIGHTCRAFT_LIBRARY`, `LIGHTCRAFT_CONTROL_PORT`, `LIGHTCRAFT_CONFIG_DIR` (isolated settings/logs/model profile), `LIGHTCRAFT_LANGUAGE=de|ja|zh-hans|…`, `LIGHTCRAFT_GPU_BACKEND=dx12|vulkan|metal|auto|off`, `LIGHTCRAFT_GPU=0`, `LIGHTCRAFT_LOG=info|debug` / `RUST_LOG`, `LIGHTCRAFT_MEMORY_MB`, `LIGHTCRAFT_NO_PREFS`, `LIGHTCRAFT_NO_NATIVE_MENU`, `LIGHTCRAFT_SAM3_DIR|_MIRRORS|_TOKEN|_REMOTE`, `LIGHTCRAFT_LENSFUN_DIR`, `LIGHTCRAFT_CAMERA_PROFILES`, `LIGHTCRAFT_DENOISE_MODELS|_URL|_TOKEN`, `LIGHTCRAFT_HDR_DISPLAY=0`, `LIGHTCRAFT_FPS=1` (start with the frame-rate overlay on), `LIGHTCRAFT_PROFILE=1` (`apps/lightcraft/src/main.rs:806-815`; `crates/ui-egui/src/frame_rate.rs:1`; other env vars in `crates/engine` and `crates/segment`). Logs go to the settings folder's `logs/lightcraft.log`, rotated on each start.
- **The CLI is a separate download on macOS.** The `.app` contains only the desktop app; there is no `lightcraft-cli` inside it and none on a stock `PATH`. Get `lightcraft-cli-<ver>-macos-universal.zip` from the release assets or build it with `cargo build --release -p lightcraft-cli`. Use the CLI and the app from the same release (`docs/mcp.md` "From an installed release"; `packaging/macos/package.sh:213-227`).
+ **The CLI is a separate download on macOS.** The `.app` contains only the desktop app; there is no `lightcraft-cli` inside it and none on a stock `PATH`. Get `lightcraft-cli-<ver>-macos-universal.zip` from the release assets or build it with `cargo build --release -p lightcraft-cli`. Use the CLI and the app from the same release (`docs/mcp.md` "From an installed release"; `packaging/macos/package.sh`).
- **Window/document model.** One process = one open library; a main window (grid or Detail) plus an optional second window (`⌘F11`). There are no per-photo documents and no tabs. Switching libraries is File ▸ Open Library… (`app.openLibrary`, `⌘O`); if a library cannot be opened, the app starts an empty session and asks (`ui.inspect` → `libraryProblem`). Pointing Open Library at a Lightroom catalog folder offers the importer instead (`docs/control-protocol.md`; `crates/ui-egui/src/panels/library_problem.rs`).
+ **Window/document model.** One process = one open library; a main window (grid or Detail) plus an optional second window (`⌘F11`). There are no per-photo documents and no tabs. Switching libraries is File ▸ Open Library… (`app.openLibrary`, `⌘O`) or File ▸ New Library… (`app.newLibrary`); if a library cannot be opened, the app starts an empty session and asks (`ui.inspect` → `libraryProblem`). Pointing Open Library at a Lightroom catalog folder offers the importer instead (`docs/control-protocol.md`; `crates/ui-egui/src/panels/library_problem.rs`).
- **Keyboard (verified in the command registry and `shortcuts.rs` — do not invent others).** Views: `G` grid, `D` detail, `N` survey, `⇧C` compare, `⇧R` reference, `F` full-screen preview, `⇧L` Lights Out, `⇧F` filter bar, `/` filmstrip, `F6`/`F7`/`F8` filmstrip/left/right panel, `\` Filter Bar in grids (Show Original in Detail). Panels/tools: `E` Edit, `C` Crop & Rotate, `M` Masking, `H` Remove, `I` Info, `K` Keywords, `⇧P` Presets, `⇧V` Versions, `B`/`L`/`R`/`W` brush/linear/radial/WB picker, `S` soft proof. View aids: `Z` zoom, `J` clipping, `A` Visualize Spots, `O` mask overlay, `⇧O` cycle crop overlay, `⇧H` tool overlay, `⌘J` View Options, `⌘I` info overlay, `⌥F` focus point, `Y`/`⇧Y` before/after. Rating: `0`–`5`, `6`–`9` labels, `P` pick, `X` reject, `U` unflag, `` ` `` cycle flag, `[`/`]` step rating, `⇧0-9/P/X/U` apply-and-advance, `⌘↑`/`⌘↓` flag up/down. Misc: `Enter` open/done, `Esc` back, `⌘Z` undo, `⌘/` shortcut editor, `⌘⇧I` import, `⌘⇧E` Edit in PhotoCraft, `⌘⌥E` Edit in Other Editor, `⌘P` print, `⌘⌥↵` slideshow. **All keys are user-remappable** (`app.setShortcut`); `⌘,` and `⌘Q` are reserved (`crates/ui-egui/src/shortcuts.rs:17`; `crates/ui-egui/src/menus.rs`; `docs/library-shortcuts.md`).
+ **Keyboard (verified in the command registry and `crates/ui-egui/src/shortcuts.rs` — do not invent others).** Views: `G` grid, `D` detail, `N` survey, `⇧C` compare, `⇧R` reference, `F` full-screen preview, `⇧L` Lights Out, `⇧F` filter bar, `/` filmstrip, `F6`/`F7`/`F8` filmstrip/left/right panel, `\` Filter Bar in grids (Show Original in Detail). Panels/tools: `E` Edit, `C` Crop & Rotate, `M` Masking, `H` Remove, `I` Info, `K` Keywords, `⌘K` Add Keywords, `⇧P` Presets, `⇧V` Versions, `B`/`L`/`R`/`W` brush/linear/radial/WB picker, `A` aspect lock (crop) / Visualize Spots, `S` soft proof (in grids `S` toggles the stack), `Shift+S` stack set-top. View aids: `Z` zoom, `J` clipping, `Shift+O` crop overlay, `⇧H` tool overlay, `⌘J` View Options, `⌘I` info overlay, `⌥F` focus point, `Y`/`⇧Y`/`⌥Y` before/after. Rating: `0`–`5`, `6`–`9` labels, `P` pick, `X` reject, `U` unflag, `` ` `` cycle flag, `[`/`]` step rating, `⌘↑`/`⌘↓` flag up/down, `⇧0-9/P/X/U` apply-and-advance. Keywords: `⌥1`–`⌥9` apply a set member, `⌥0`/`⌥⇧0` next/previous set. Misc: `Enter` open/done, `Esc` back, `⌘Z` undo, `⌘L` Enable Filters, `⌘⌥⇧C`/`⌘⌥⇧V` copy/paste metadata, `⌘⇧I` import, `⌘⇧E` Edit in PhotoCraft, `⌘⌥E` Edit in Other Editor, `⌘P` print, `⌘⌥↵` slideshow. **All keys are user-remappable** (`app.setShortcut`); `⌘,` and `⌘Q` are reserved (`crates/ui-egui/src/shortcuts.rs:17`; `crates/ui-egui/src/menus.rs`; `docs/library-shortcuts.md`).
- **Open / save / export.** Import with File ▸ Import Photos… (`⌘⇧I`, `file.addPhotos`) or by passing paths on the command line. There is nothing to "save": each command persists. `⌘S` (`photo.saveMetadataToFile`, Photo ▸ Save Metadata to File) writes sidecars; automatic sidecar writing is File ▸ Automatically Write Changes into XMP (`library.toggleAutoWriteXmp`, also a Settings ▸ General checkbox, `settings.autoWriteXmp`), both driving `library.xmpPreferences`. Export with File ▸ Export… (`dialog.export`), Photo ▸ Edit In (`⌘⇧E` / `⌘⌥E`), File ▸ Contact Sheet PDF…, File ▸ Print… (`⌘P`; its dialog is titled "Print to PDF…" and writes a colour-managed PDF), View ▸ Export Slideshow…, or the equivalent commands. The photo export itself is always `app.export` with the options above (`crates/engine/src/export.rs:619-754`; `crates/engine/src/cmd/xmp.rs:123`; `crates/ui-egui/src/panels/settings.rs:357-359`; `crates/ui-egui/src/panels/dialogs.rs:1355`).
+ **Open / save / export.** Import with File ▸ Import Photos… (`⌘⇧I`, `file.addPhotos`), Import from Folder… (`file.addFolder`), or by passing paths on the command line; files dropped on the window go through the import review (`ui.dropFiles` reproduces that headlessly). There is nothing to "save": each command persists. `⌘S` (`photo.saveMetadataToFile`, Photo ▸ Save Metadata to File) writes sidecars; automatic sidecar writing is File ▸ Automatically Write Changes into XMP (`library.toggleAutoWriteXmp`), both driving `library.xmpPreferences`. Export with File ▸ Export… (`dialog.export`), Photo ▸ Edit In (`⌘⇧E` / `⌘⌥E`), File ▸ Contact Sheet PDF…, File ▸ Print… (`⌘P`; its dialog is titled "Print to PDF…" and writes a colour-managed PDF), View ▸ Export Slideshow…, or the equivalent commands. The photo export itself is always `app.export` with the options above (`crates/engine/src/export.rs:627-870`; `crates/engine/src/cmd/xmp.rs`).
**Drive it — the three supported routes**
1. One-shot CLI (best for batch work, no session state):
```sh
lightcraft-cli run --import ./shoot develop.set control=light.exposure value=0.7 \
develop.auto app.export dir=out longEdge=2048 colorSpace=displayP3
lightcraft-cli run --library ./lib library.select ids=[3] develop.get
lightcraft-cli run --demo --script steps.jsonl --keep-going # JSON lines; stdin works too
lightcraft-cli run --connect 127.0.0.1:7980 ui.set view=detail ui.screenshot path=ui.png
lightcraft-cli render in.dng -o out.jpg --set light.exposure=0.5 --size 2048 --quality 92
- lightcraft-cli render in.dng -o out.tif --opt colorSpace=displayP3 --opt bitDepth=16
lightcraft-cli merge hdr-batch ./brackets # every bracket set in the folder
```
One JSON line per command (`{command, ok, result|error, ms}`); non-zero exit on failure; a bare word starts the next command, `key=value` values parse as JSON when they can (`docs/mcp.md` "One-shot commands"; `apps/lightcraft-cli/src/main.rs` USAGE).
2. Control channel (best for interacting with a live UI or an external controller):
```json
{"id":1,"method":"engine.execute","params":{"command":"develop.set","params":{"values":{"light.highlights":-45}}}}
+ {"id":2,"method":"ui.dropFiles","params":{"paths":["./shoot"]}}
```
Discover identifiers with `engine.commands`, `develop.controls`, `ui.widgets`; never hard-code translated labels or widget positions. Group slider bursts in `develop.beginInteraction` / `develop.endInteraction` (one undo step). On macOS `ui.key` injects egui events and does **not** synthesize Cocoa menu accelerators; use `ui.menu.invoke` with the command id when testing menu shortcuts (`docs/control-protocol.md` "Methods").
3. MCP (`lightcraft-cli mcp`): helpers plus `command_list` / `command_run` / `command_batch` / `doc_inspect` / `render_preview`, 7 resources, and one generated tool per command unless `--compact`. In connect mode the UI tools are added; in headless mode they return an error explaining how to start the app (`docs/mcp.md`).
**Look at the result.** Any UI work must be verified visually: `lightcraft-cli snapshot --demo --script tour.jsonl -o out.png` or `{"method":"ui.screenshot","params":{"path":"a.png","headless":true}}`. Before capturing, poll `ui.inspect` until the expected `loupe.photo` is active, `loupe.source` is `render` or `cached`, `loupe.pending` is false and the render queues are empty — or `loupe.failed` is set (`docs/control-protocol.md` "Before capturing a finished Detail view").
## Working with an AI agent
**Legitimate agent work**
- - Pre-generate or convert inputs into supported formats before touching the library. There is no ingest conversion step for unsupported files: an unsupported raw/AVIF stays unsupported unless you convert it externally. HEIC works only in a build with the `heif` feature (`crates/engine/src/import.rs:37-44`; `crates/codecs/Cargo.toml:43-46`).
+ - Pre-generate or convert inputs into supported formats before touching the library. There is no ingest conversion step for unsupported files: an unsupported raw/AVIF stays unsupported unless you convert it externally. HEIC works only in a build with the `heif` feature (official macOS packaging has it) (`crates/engine/src/import.rs:37-44`; `crates/codecs/Cargo.toml:43-46`).
- Batch develop and export through `lightcraft-cli run --import …`, or against a persistent library with `--library DIR` so ratings and edits survive between calls (`docs/mcp.md`).
- Batch-render one image with a settings JSON: `lightcraft-cli render in.dng -o out.jpg --settings look.json` (a partial develop-settings object, or a single-preset `.lcpreset`/XMP file) plus `--set control=value` and `--opt` for export options (`apps/lightcraft-cli/src/main.rs` USAGE).
- - Inspect and repair library files: `catalog.snap` and `catalog.log` are documented, CRC-checked JSON; the loader already handles torn appends and damaged records. Back the folder up before editing anything by hand (`crates/catalog/src/journal.rs:1-30`).
+ - Inspect and repair library files: `catalog.snap` and `catalog.log` are documented, CRC-checked JSON; the loader already handles torn appends and damaged records. Back the folder up before editing anything by hand (`crates/catalog/src/journal.rs:1-40`).
- Verify exports by reading the returned `{path, width, height, bytes, sidecars}` and then the file itself; the encoder is pure (bytes in, bytes out) so it is testable without a window (`crates/engine/src/export.rs:1-5`).
- - Read XMP sidecars to understand another tool's edits; LightCraft preserves foreign namespaces. Do not expect LightCraft to write face regions to XMP (`docs/xmp-interop.md`; `crates/catalog/src/model.rs:196-199`).
- - Rebuild camera colour profiles from a user's own raws with `lightcraft-cli calibrate`; enable lens corrections from the user's own lensfun database by setting `LIGHTCRAFT_LENSFUN_DIR` before launch (`apps/lightcraft-cli/src/main.rs` USAGE; `crates/engine/src/lens_profiles.rs:15`).
- - Drive output modules as commands: `export.contactSheet`, the `print.*` family, `slideshow.*` and `export.addToPhotos`, with `activity.list` progress (`crates/engine/src/cmd/{export,print,slideshow}.rs`; `crates/engine/src/cmd/activity.rs`).
+ - Read XMP sidecars to understand another tool's edits; LightCraft preserves foreign namespaces. Do not expect LightCraft to write face regions to XMP (`docs/xmp-interop.md`).
+ - Rebuild camera colour profiles from a user's own raws with `lightcraft-cli calibrate`; enable lens corrections from the user's own lensfun database by setting `LIGHTCRAFT_LENSFUN_DIR` before launch (`apps/lightcraft-cli/src/main.rs` USAGE; `crates/engine/src/lens_profiles.rs`).
+ - Drive output modules as commands: `export.contactSheet`, the `print.*` family (including `.lcprint` template import/export), `slideshow.*` and `export.addToPhotos`, with `activity.list` progress (`crates/engine/src/cmd/export.rs`, `crates/engine/src/cmd/print.rs`, `crates/engine/src/cmd/slideshow.rs`; `crates/engine/src/cmd/activity.rs`).
+ - Choose where a Lightroom catalog goes before importing: `file.importLightroom {into: "open"|"new"}` (UI command) or run `library.importLightroom` after opening the target library (`crates/ui-egui/src/lightroom_destination.rs:1-30`).
- Query and edit through MCP or the control channel using **command ids and control ids** from the registry, not translated labels; use `ui.inspect`/`ui.widgets` and automation ids for UI state (`docs/control-protocol.md`; `docs/mcp.md`).
- Install/point at optional models only with the user's consent: SAM 3 (Hugging Face gated; 3.4 GB) and denoise ONNX weights; face models are separate (`docs/ai-masks.md`; `docs/denoise.md`; `docs/faces.md`).
**Do not**
- Do not launch the GUI and drive it with OS-level synthetic input (osascript/System Events, cliclick): it goes to whatever window is frontmost. Use the control channel, `lightcraft-cli snapshot`, or MCP connect mode (repo `AGENTS.md`: "Never send OS-level synthetic input").
- Do not assume the CLI exists — on macOS it is a separate download, not inside the `.app` (`docs/mcp.md` "From an installed release").
- - Do not assume a library can be opened by two programs at once (`catalog.lock`); use `--connect` instead (`crates/catalog/src/lock.rs:84-88`).
+ - Do not assume a library can be opened by two programs at once (`catalog.lock`); use `--connect` instead (`crates/catalog/src/lock.rs:86`).
- Do not assume Adobe assets or formats work: `.dcp`, `.lcp`, Lightroom plug-ins, `.lrlibrary`. `.lrtemplate` is import-only; `.dcp`/`.lcp` are deliberately unsupported (`docs/file-format-parity.md`).
- Do not promise AI subject/sky masks, AI denoise, face recognition or super resolution out of the box — they need optional models, and the default SAM mirror list is empty (`docs/ai-masks.md`; `docs/denoise.md`; `docs/gaps.md:28-29`).
- - Do not promise that a raw will develop from sensor data; check the format list above and expect preview-only for compressed ORF, HE Nikon NEF, some CR3 variants and compressed SRW (`docs/raw-parity.md:140-185`).
+ - Do not promise that a raw will develop from sensor data; check the format list above and expect preview-only for compressed ORF, HE Nikon NEF, some CR3 variants and compressed SRW (`docs/raw-parity.md`).
- Do not treat a returned command result as proof of a correct image. Nothing here validates the pixels (`docs/gaps.md:20`).
- Do not overwrite a catalogued original or its XMP sidecar: MCP `export`/`render_photo`/`screenshot` refuse such a `path`; use a new output folder (`docs/mcp.md` export helper).
## Verification protocol
LightCraft is alpha; its library is crash-safe but its output is not self-checking. For every non-trivial task:
- 1. **Throwaway round-trip first.** Make a scratch library (`--library` pointed at a fresh folder, or `lightcraft-cli run --demo`), import one file, change one slider, export, then reopen the export in LightCraft or any other viewer and confirm dimensions and pixels. Only then run the real batch. Use `--memory` when you only want to experiment — it writes nothing (`apps/lightcraft/src/main.rs:766-790`; `apps/lightcraft-cli/src/main.rs` USAGE).
- 2. **Never trust a silent export.** `app.export` reports per-file `{path, width, height, bytes, sidecars}` and can report `gpuFallback`; a size/resolution mismatch or a zero-byte file is the failure you would otherwise miss. `format: "original"` also writes an XMP sidecar — check for it before assuming the folder is unchanged (`crates/engine/src/export.rs:619-754`; `docs/mcp.md` export helper).
+ 1. **Throwaway round-trip first.** Make a scratch library (`--library` pointed at a fresh folder, or `lightcraft-cli run --demo`), import one file, change one slider, export, then reopen the export in LightCraft or any other viewer and confirm dimensions and pixels. Only then run the real batch. Use `--memory` when you only want to experiment — it writes nothing (`apps/lightcraft/src/main.rs:793-815`; `apps/lightcraft-cli/src/main.rs` USAGE).
+ 2. **Never trust a silent export.** `app.export` reports per-file `{path, width, height, bytes, sidecars}` and can report `gpuFallback`; a size/resolution mismatch or a zero-byte file is the failure you would otherwise miss. `format: "original"` also writes an XMP sidecar — check for it before assuming the folder is unchanged (`crates/engine/src/export.rs:627-870`; `docs/mcp.md` export helper).
3. **Check the render, not just the command.** `ok: true` means the command ran. Poll `ui.inspect` for `loupe.pending == false` (or `loupe.failed` for the reason) and take a screenshot/PNG before declaring success (`docs/control-protocol.md` "Before capturing a finished Detail view").
- 4. **Version and back up per session.** Copy the whole library folder before a bulk operation; the catalog is an append-only log, so a copied folder is a complete restore point. On the desktop there is **no in-app backup command** — `file.backupLibrary` / `file.restoreLibrary` are wired only in the web build, where the library lives in browser storage (`apps/lightcraft/src/main.rs:713-715`; `crates/ui-egui/src/menubar.rs:1157`). Keep the app and CLI from the same release.
+ 4. **Version and back up per session.** Copy the whole library folder before a bulk operation; the catalog is an append-only log, so a copied folder is a complete restore point. On the desktop there is **no in-app backup command** — `file.backupLibrary` / `file.restoreLibrary` are wired only where the platform supplies a backup service (the web build); on desktop the command returns "the library is a folder; back it up with your other files" (`crates/ui-egui/src/menus.rs:863-868`). Keep the app and CLI from the same release.
5. **Update between projects, never mid-project.** Re-run step 1 after any update — the launcher auto-updates, and a new build can change rendering; process versions exist for exactly that reason (`docs/process-versions.md`).
- 6. **Know your single-writer rule.** Any `--library` headless run fails while the desktop app holds that library. Reserve it in your plan, or drive the open app with `--connect` (`docs/mcp.md:19-22`; `crates/catalog/src/lock.rs:84-88`).
- 7. **Treat the control port as a local privilege.** It has no authentication and can run any command; only enable it for a task that needs it, on loopback (`docs/control-protocol.md:27`).
+ 6. **Know your single-writer rule.** Any `--library` headless run fails while the desktop app holds that library. Reserve it in your plan, or drive the open app with `--connect` (`docs/mcp.md:20`; `crates/catalog/src/lock.rs:86`).
+ 7. **Treat the control port as a local privilege.** It has no authentication and can run any command; only enable it for a task that needs it, on loopback (`docs/control-protocol.md:39`).
## Sources & evidence
- - Repo: https://github.com/storytold/lightcraft — inspected tag **v0.12.0**, commit **`479a05a0be4756be460859c4d42b4fbb7a60e99d`** (detached worktree at the tag; `git rev-parse HEAD`), 2026-10-23. Target: Lightroom Classic 15.5 / Lightroom 9.6 (`ROADMAP.md:5`). License MIT OR Apache-2.0.
- - Installed `/Applications/LightCraft.app`: `CFBundleShortVersionString` 0.12.0, `LightCraftBuildCommit` `479a05a0be4756be460859c4d42b4fbb7a60e99d`, binary `--version` prints `lightcraft 0.12.0`, bundle id `ai.storyteller.lightcraft`; the bundle has no `CFBundleDocumentTypes`. The binary contains `lightcraft_heif`/`heic_rs` symbols (HEIC enabled in the official build). **Verify against your installed version** if the launcher has updated since.
- - Files read for this skill: `README.md`, `ROADMAP.md`, `Cargo.toml`, `AGENTS.md`, `apps/lightcraft/Cargo.toml`, `apps/lightcraft/src/main.rs`, `apps/lightcraft/src/control_server.rs`, `apps/lightcraft-cli/src/main.rs`, `crates/engine/src/{import,export,library,presets,rename,lens_profiles,camera_profiles,contact_sheet}.rs`, `crates/engine/src/cmd/{mod,library,develop,masks,export,print,slideshow,merge,sync,xmp,convert,preset_files,browse,geocode,cull}.rs`, `crates/engine/src/{print_pdf,print/layout,slideshow_export}.rs`, `crates/develop/src/{controls,settings}.rs`, `crates/catalog/src/{journal,lock,model}.rs`, `crates/raw/src/{lib,spectral,spectral_table,kelvin_builtin}.rs`, `crates/raw/src/vendor/{mrw,nef,rw2,raf_lens,arw_lens,arw_lens_rm6,nef_lens}.rs`, `crates/mcp/src/{tools,server}.rs`, `crates/lensdb/src/lib.rs`, `crates/codecs/src/{psd,encode}.rs`, `crates/ui-egui/src/{menus,shortcuts,i18n}.rs`, `packaging/macos/{Info.plist.in,package.sh}`, `assets/camera-profiles/`, and `docs/`: `target-app-parity`, `raw-parity`, `file-format-parity`, `gaps`, `control-protocol`, `mcp`, `merge`, `slideshow`, `denoise`, `ai-masks`, `faces`, `focus-points`, `xmp-interop`, `process-versions`, `lens-profiles`, `sony-lens-corrections`, `sony-ilce7rm6-lens-corrections`, `nikon-lens-corrections`, `fuji-lens-corrections`, `camera-preview-colour`, `web`, `localization`, `localization-parity`, `background-tasks`, `raw-preview-reads`, `lightroom-catalog-import`, `display-profiles`, `apple-photos`, `library-shortcuts`, `whats-new`, `parity-checklist` (all `.md`).
+ - Repo: https://github.com/storytold/lightcraft — inspected tag **v0.14.0**, commit **`03c88c166c054c009db2a47187d59991f6232b85`** (detached worktree at the tag; `git rev-parse HEAD`), 2026-10-26. Target: Lightroom Classic 15.5 / Lightroom 9.6 (`ROADMAP.md:3`). License MIT OR Apache-2.0.
+ - Installed `/Applications/LightCraft.app`: `CFBundleShortVersionString` 0.14.0, `LightCraftBuildCommit` `03c88c166c054c009db2a47187d59991f6232b85`, binary `--version` prints `lightcraft 0.14.0`, bundle id `ai.storyteller.lightcraft`; the bundle has no `CFBundleDocumentTypes`. The binary contains `lightcraft_heif::decode|probe|decode_thumbnail` (HEIC enabled in the official build). **Verify against your installed version** if the launcher has updated since.
+ - Code files read for this skill: `README.md`, `ROADMAP.md`, `Cargo.toml`, `AGENTS.md`, `apps/lightcraft/src/main.rs`, `apps/lightcraft/src/control_server.rs`, `apps/lightcraft/src/native_menu.rs`, `apps/lightcraft-cli/src/main.rs`, `apps/lightcraft-web/index.html`, `apps/lightcraft-web/src/web.rs`, `apps/lightcraft-web/src/automation.rs`, `crates/engine/src/library.rs`, `crates/engine/src/import.rs`, `crates/engine/src/export.rs`, `crates/engine/src/presets.rs`, `crates/engine/src/camera_profiles.rs`, `crates/engine/src/rename.rs`, `crates/engine/src/contact_sheet.rs`, `crates/engine/src/print_pdf.rs`, `crates/engine/src/slideshow_export.rs`, `crates/engine/src/focus_scan.rs`, `crates/engine/src/print/layout.rs`, `crates/engine/src/print/template.rs`, `crates/engine/src/cmd/mod.rs`, `crates/engine/src/cmd/library.rs`, `crates/engine/src/cmd/develop.rs`, `crates/engine/src/cmd/masks.rs`, `crates/engine/src/cmd/keywords.rs`, `crates/engine/src/cmd/print.rs`, `crates/engine/src/cmd/export.rs`, `crates/engine/src/cmd/slideshow.rs`, `crates/engine/src/cmd/merge.rs`, `crates/engine/src/cmd/sync.rs`, `crates/engine/src/cmd/xmp.rs`, `crates/engine/src/cmd/convert.rs`, `crates/engine/src/cmd/browse.rs`, `crates/engine/src/cmd/geocode.rs`, `crates/engine/src/cmd/cull.rs`, `crates/engine/src/cmd/manage.rs`, `crates/engine/src/cmd/previews.rs`, `crates/engine/src/cmd/activity.rs`, `crates/engine/src/cmd/metadata.rs`, `crates/develop/src/controls.rs`, `crates/develop/src/settings.rs`, `crates/catalog/src/journal.rs`, `crates/catalog/src/lock.rs`, `crates/catalog/src/model.rs`, `crates/raw/src/lib.rs`, `crates/raw/src/spectral.rs`, `crates/raw/src/spectral_table.rs`, `crates/raw/src/vendor/nef.rs`, `crates/raw/src/vendor/rw2.rs`, `crates/raw/src/vendor/mrw.rs`, `crates/raw/src/vendor/raf_lens.rs`, `crates/raw/src/vendor/arw_lens.rs`, `crates/raw/src/vendor/arw_lens_rm6.rs`, `crates/raw/src/vendor/nef_lens.rs`, `crates/mcp/src/tools.rs`, `crates/mcp/src/server.rs`, `crates/lensdb/src/lib.rs`, `crates/codecs/src/psd.rs`, `crates/codecs/src/sniff.rs`, `crates/codecs/Cargo.toml`, `crates/meta/src/psd.rs`, `crates/meta/src/xmp.rs`, `crates/merge/src/pano/mod.rs`, `crates/merge/src/flow.rs`, `crates/merge/src/refine.rs`, `crates/inpaint/src/lib.rs`, `crates/ui-egui/src/menus.rs`, `crates/ui-egui/src/shortcuts.rs`, `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/src/control.rs`, `crates/ui-egui/src/menubar.rs`, `crates/ui-egui/src/lightroom_destination.rs`, `crates/ui-egui/src/frame_rate.rs`, `crates/ui-egui/src/drop_overlay.rs`, `crates/ui-egui/src/panels/settings.rs`, `packaging/macos/Info.plist.in`, `packaging/macos/package.sh`, `assets/camera-profiles/`, `.github/workflows/ci.yml`, `.github/workflows/freebsd.yml`.
+ - Docs read for this skill: `docs/target-app-parity.md`, `docs/raw-parity.md`, `docs/file-format-parity.md`, `docs/gaps.md`, `docs/control-protocol.md`, `docs/mcp.md`, `docs/merge.md`, `docs/slideshow.md`, `docs/denoise.md`, `docs/ai-masks.md`, `docs/faces.md`, `docs/focus-points.md`, `docs/xmp-interop.md`, `docs/process-versions.md`, `docs/lens-profiles.md`, `docs/sony-lens-corrections.md`, `docs/sony-ilce7rm6-lens-corrections.md`, `docs/nikon-lens-corrections.md`, `docs/fuji-lens-corrections.md`, `docs/camera-preview-colour.md`, `docs/web.md`, `docs/localization.md`, `docs/localization-parity.md`, `docs/background-tasks.md`, `docs/raw-preview-reads.md`, `docs/lightroom-catalog-import.md`, `docs/display-profiles.md`, `docs/apple-photos.md`, `docs/library-shortcuts.md`, `docs/whats-new.md`, `docs/parity-checklist.md`, `docs/cr3.md`, `docs/ui-parity.md`, `docs/hardware-parity.md`, `docs/raw/coverage.tsv`, and `docs/user/index.md` plus the new `docs/user/getting-started/`, `docs/user/guides/`, `docs/user/faq/`, `docs/user/troubleshooting/` trees.
+ - Counts were measured from source (not from README/docs or the previous skill): 375 engine commands (37 `specs()` sources), 190 `UI_COMMANDS` + 18 language commands = 208 UI entries (583 total), 17/28 MCP helper tools, 7 MCP resources, 392/611 `tools/list` entries, 24 control arms / 29 method names, 10 CLI subcommands, 39 import extensions, 10 export formats, 122 + 10 develop controls, 41 presets, 22 develop profiles, 8 camera profiles, 52 spectral matrices, 18 languages, 1,446 Adobe raw models (551/656/202/37).
- Shared library `storytold/craft-libs` (read-only reference) provides `craft-ui` (pinned rev `5c874d51…`) and `craft-docpath` (pinned rev `82a24b0…`) (`Cargo.toml:13-14`); LightCraft's own formats are documented in this repo, so nothing outside it was needed.
- Evidence tiers: **implemented** (the code path exists and is reachable from UI/CLI/command), **partial/stub** (exists but visibly incomplete — what is missing is stated), **not implemented** (absent or explicitly out of scope).
**Uncertain / not verified in this pass**
- - Runtime GUI behaviour, menus and dialogs were not launched: every GUI claim comes from the command registry, the control-protocol document, the parity checklist and the packaging metadata. The installed binary was only inspected read-only (`--version`, `nm`, `Info.plist`).
+ - Runtime GUI behaviour, menus and dialogs were not launched: every GUI claim comes from the command registry, the control-protocol document, the parity checklist and the packaging metadata. The installed binary was inspected read-only (`--version`, `nm`, and `/Applications/LightCraft.app/Contents/Info.plist`).
- The exact visual/colour fidelity of renders is not compared against Lightroom; the project itself says it is unmeasured (`docs/gaps.md:20`).
- - HEIC availability depends on the build: official packaging passes `--features heif`, but a source build without it does not decode HEIC/HEIF. The installed 0.12.0 binary has the symbols.
- - MCP tool totals are derived statically from `helper_tools` and the command count, not captured from a running `tools/list`; connect-mode totals assume `engine.commands` reports all 204 UI entries (186 `UI_COMMANDS` + 18 language commands), which `all_commands` in `crates/ui-egui/src/control.rs:78-97` does.
+ - HEIC availability depends on the build: official packaging passes `--features heif` and the installed binary has the symbols; a source build without the feature does not decode HEIC/HEIF.
+ - MCP tool totals are derived statically from `helper_tools` and the measured command counts, not captured from a running `tools/list`; connect-mode totals assume `engine.commands` reports all 208 UI entries, which `all_commands` in `crates/ui-egui/src/control.rs` does.
- Whether a specific raw variant decodes can only be settled file-by-file; the per-model table (`docs/raw/coverage.tsv`) is evidence, not a guarantee for every body that writes the container.
- - Two project docs disagree with code at this tag and were followed from code instead: "5 bundled profiles" vs 8 (`assets/camera-profiles/`), and 201 preview / 38 unsupported vs the generated table's 202 / 37.
+ - Where project docs disagree with code at this tag, code was followed and the drift is listed above: "5 bundled profiles" vs 8 (`assets/camera-profiles/`), 201 preview / 38 unsupported vs the generated table's 202 / 37, ROADMAP 81.6%/532 vs the generated checklist 83.5%/543, the `heif` release comment vs `packaging/macos/package.sh`, and Vietnamese listed as absent vs the shipped locale.