Files
lightcraft/plan/phase-4.md
T
Andrew FrahnandClaude Opus 5.5 6cbc0eb5a9 Phase 0: configurable brand, MIT licence, plan/ and Immich tracker
- brand.toml is the only place the product name lives; every crate and app
  reads it through dac-brand (env vars with legacy fallback, settings folder
  with copy-only migration, default library, preset extensions, i18n {app})
- cargo xtask brand check|test, run|install|package, docs, rename-crates,
  deny and immich; packaging files are templates rendered from brand.toml
- icons regenerated from brand/icon.svg; upstream branding removed
- MIT licence (segment and fetch stay Apache-2.0); cargo deny licence policy
- clean-room rules in AGENTS.md/CLAUDE.md; docs/upstream-merge.md
- plan/ rebuilt and committed; parity tracker reopens Classic rows, adds
  PLAN.md section 2 rows and the IMM Immich section; plan/immich.md and a
  pinned Immich test server

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 10:30:06 +11:00

11 KiB
Raw Blame History

Phase 4: Workflow power features and full Immich integration

Copy of PLAN_phase_4.md at the repository root, taken 2026-10-10. The root file is the working copy; refresh this one when it changes.

Part of PLAN.md. Estimate: 60–105 agent-hours (including about 20–35 h of Immich work). Depends on: Phase 1 (catalog v4, net, credentials, the immich crate and links). Runs in parallel with: Phases 2 and 3.

Goal:

  • the workflow features power users rely on: tethering, publish services, plugins, round trips, actions, people and catalog migration;
  • two-way integration with Immich, so the desktop catalog and a self-hosted Immich server stay one library.

4.1 Tethered capture (≈ 10–16 h)

  1. Studio capture first (the design described in Ansel's prose note doc/studio-capture.md): watch a folder written by any vendor tool or the camera's own app; auto-import with:
    • a develop preset;
    • a metadata preset and keywords;
    • a session name and naming template;
    • a target collection. The newest shot shows in the loupe automatically.
  2. Native tethering, new crate tether:
    • PTP over USB with nusb (pure Rust), plus PTP/IP over Wi-Fi;
    • vendor extensions for Canon, Nikon, Sony and Fujifilm, in that order;
    • capture, download-on-capture, delete-from-card option.
  3. Tether bar (as in Classic): camera name, shutter, aperture, ISO and WB readouts and settings; capture button; develop settings "same as previous"; live view where the camera supports it.
  4. Platform notes:
    • Linux needs a udev rule (shipped in packages).
    • macOS: release the camera from ptpcamerad.
    • Windows: WinUSB driver notes.
    • Unsupported cameras fall back to studio capture with a clear message.

Done when: studio capture works with any camera; native capture is verified on at least one Canon, Nikon and Sony body; CI uses a simulated PTP device.


4.2 Publish services framework (≈ 8–12 h)

  • Model:
    • a publish service (type plus account plus export settings) contains published collections and sets;
    • each photo has a state: new to publish, modified to re-publish, published, deleted to remove;
    • remote ids are stored in remote_identity (Phase 1.7);
    • the modified state comes from the develop/metadata change feed (the catalog op log).
  • UI:
    • Publish Services panel in Library;
    • the Publish button;
    • the grid split into New / Modified / Published sections;
    • "Mark as Up-to-date";
    • remote comments and likes where the service supports them.
    • This closes LRC-LIB-COMMENTS for services that have them.
  • Built-in services:
    • Hard Drive (export to a folder kept in sync);
    • Immich (4.6);
    • SFTP (reuses Phase 3.6).
  • Other services (Flickr, SmugMug, Piwigo, Nextcloud, PhotoPrism, WebDAV) come as plugins (4.3).

4.3 Plugin SDK (≈ 10–16 h)

  • Sandbox: port PhotoCraft's plugins crate (wasmi, no host imports by default, fuel and memory limits).
  • Host API, versioned and capability-gated:
    • catalog query (read);
    • metadata read/write in a plugin-owned namespace, plus standard fields with permission;
    • export post-process hooks;
    • publish service provider interface (the same trait the built-in services implement);
    • metadata/keyword providers;
    • menu commands that register as engine commands;
    • simple dialogs from a declarative UI description.
  • Permissions:
    • network per host;
    • filesystem roots;
    • granted on install and revocable in the Plugin Manager.
  • Plugin Manager: install, enable, disable, update, view logs.
  • SDK: Rust crate plus template, docs and two example plugins (a WebDAV publisher and a metadata reverse-lookup).
  • Lightroom's Lua plugins are not supported, and no promise is made to support them.

4.4 Edit In and the PhotoCraft round trip (≈ 5–8 h)

  • External editor presets (app, file format, colour space, bit depth, resolution, compression, stack with original, naming).
  • PhotoCraft integration:
    • "Edit in PhotoCraft" sends TIFF or layered PSD using PhotoCraft's psd writer (ported). "Open as Layers" builds a layered PSD from the selection.
    • When the file is saved, it is re-imported and stacked with the original; the edit-in round trip keeps metadata.
    • If PhotoCraft runs with --control, the two apps can talk directly over the control channel: open, and notify on save.
  • PSD/PSB import uses the PhotoCraft psd reader (flattened composite plus layer metadata).

4.5 Actions, people, migration (≈ 10–18 h)

  • Actions:
    • record a sequence of engine commands (built on PhotoCraft's actions_cmds design and our journal);
    • parameterise it, save it, assign a shortcut, run it on a selection;
    • also exposed through MCP and the CLI (app-cli run-action).
  • People:
    • face clusters with separators and confidence;
    • merge and split;
    • small-face detection on large images (tiled detection beyond 640 px);
    • write face regions to XMP (MWG Regions) and read them back;
    • person keywords.
  • .lrcat migration v2:
    • replay develop history and snapshots;
    • a profile mapping table (Adobe profile names → our camera-matching/creative profiles from Phase 2.3);
    • masks (brush, linear, radial, range) and spot/heal;
    • a per-photo fidelity report listing what could not be mapped;
    • publish-service collections imported as plain collections.
    • Mapping is derived from the published XMP crs: field descriptions and from our own measurements: apply known values in Lightroom, export, compare (the local fidelity references of Phase 2.8). darktable's importer is not read (MIT clean-room).

4.6 Immich: full two-way integration (≈ 20–35 h)

Builds on Phase 1.8: connection, link by checksum, import, shared originals. Every Immich action is a command (immich.*), so it works from the UI, CLI, control channel and MCP (IMM-MCP).

4.6.1 IMM-PUBLISH: Immich as a publish service

  • An "Immich" publish service per connected account. Published collections map 1:1 to Immich albums (created if missing, renamed if the collection is renamed). Collection sets map to album name prefixes.
  • What is sent, per service:
    1. Rendered JPEG/AVIF/JXL using the export settings, so Immich shows the edited look; or
    2. Original (plus XMP sidecar); or
    3. Both, stacked in Immich, with the render on top and the original underneath, using Immich's stack feature.
  • Upload:
    • streaming multipart upload with progress and resume-on-failure (via net);
    • a stable device asset id (catalog photo id plus virtual-copy id);
    • checksum pre-check before upload, so Immich never stores a duplicate; already-present assets are just linked and added to the album.
  • Re-publish: edits mark photos modified; re-publishing replaces the rendered asset in place (keeping its id, albums and favourites where the API allows), otherwise uploads a new asset, moves album membership and trashes the old one.
  • Removal: removing from a published collection removes the photo from the album. Deleting the Immich asset is a separate, explicit option (default off); assets go to the Immich trash, never a hard delete.
  • Shared-originals mode (IMM-EXTLIB): for originals already in an Immich external library, publish only changes album membership and metadata; nothing is uploaded except renders, if chosen.

4.6.2 IMM-SYNC: two-way metadata sync

  • Per-field opt-in mapping, with defaults:
Catalog Immich Default
Star rating 0–5 asset rating two-way
Pick flag favourite two-way (configurable: favourite ↔ pick, or favourite ↔ 5★, or off)
Reject flag archived off (configurable)
Title / Caption description two-way (caption ↔ description; title optional)
Keywords (hierarchical) tags (hierarchical, A/B/C) two-way; export-excluded keywords never sent
Collections albums two-way for linked collections only
GPS, capture date/time asset location and date two-way, with a conflict prompt
People names / face regions people / faces see IMM-PEOPLE
Deleted photo trashed asset never automatic; a queue for the user to confirm
  • Change detection:
    • local changes come from the catalog op log since last_synced_at;
    • remote changes come from incremental queries (updatedAfter or the server's sync stream, depending on the pinned Immich API version in plan/immich.md).
  • Conflicts (both sides changed since the last sync):
    • a per-field policy: catalog wins, Immich wins, or newest wins (the default);
    • a Sync Conflicts view that shows both values for anything unresolved.
  • Scheduling: manual, on publish, or a background interval. It runs offline-safe: queued and retried with backoff, and it never blocks the UI.
  • Activity log entries for every sync run; a dry-run mode shows what would change.

4.6.3 IMM-PEOPLE

  • Import Immich people (names, birthdates, hidden flag) and face regions (bounding boxes) for linked assets into the People view. They are marked "from Immich" and can be confirmed or corrected.
  • Push names assigned in the app back to Immich people (opt-in); merges map to Immich person merges.
  • This gives good face recognition before Phase 5, by reusing Immich's own machine learning.
  • The Library filter bar and the Text filter get an "Immich smart search" mode: a natural-language query (Immich's CLIP search) returns asset ids, which are mapped to linked catalog photos. The results show as a temporary collection that can be saved as a collection.
  • Also: metadata search (OCR text, objects, places) when the server supports it.
  • This stands in for local AI search (FILT-SEARCH-AI) until Phase 5, and can coexist with it later.

4.6.5 Hardening and tests

  • Version policy: support the pinned minimum Immich release and the latest release. A nightly CI job runs integration tests against both through cargo xtask immich up --version ….
  • Scale: sync of 100k assets completes, resumes after interruption and stays under 5% CPU when idle.
  • Failure:
    • server down, key revoked, permission missing, TLS change: each gives a clear error, nothing is lost, and work is queued;
    • never crash, as the never-crash standard requires; fuzz the JSON decoding of server responses.
  • Privacy: a "private" saved location (Phase 3.3) strips GPS from renders sent to Immich too. Uploads respect the export metadata policy.

Exit gate

  • Studio capture and native tethering (Canon/Nikon/Sony verified) ✅.
  • Publish framework with Hard Drive, SFTP and Immich services; plugin SDK with two example plugins.
  • Edit-in round trip with PhotoCraft (layered PSD) ✅; actions record and replay ✅.
  • People clusters and XMP face regions ✅; .lrcat v2 migration with fidelity report.
  • IMM-PUBLISH, IMM-SYNC, IMM-PEOPLE, IMM-SEARCH and IMM-MCP ✅ against the minimum and latest Immich releases.
  • Scripted end-to-end session through MCP, headless in CI:
    1. simulated tethered shoot;
    2. import preset;
    3. cull;
    4. develop;
    5. publish to Hard Drive, SFTP and Immich (album plus stacked original);
    6. edit the rating in Immich and see it sync back;
    7. print a contact sheet to PDF.