Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5c37c6c
feat(plugin): add develop-settings experiments for AI Edit via XMP
bmachek Sep 26, 2026
9538565
Merge branch 'main' into claude/ai-edit-xmp-presets-1a845a
bmachek Sep 28, 2026
52326f2
feat(plugin): extend develop experiments before the test-catalog run
bmachek Sep 28, 2026
aceca51
chore(develop): add registry tooling and scrubbed develop-settings fi…
bmachek Sep 28, 2026
df9469d
feat(develop): add lrg-develop crate with key registry, model and Lua…
bmachek Sep 29, 2026
71dedbc
fix(edit): transfer white balance by family, report warnings as a list
bmachek Sep 29, 2026
86a39ef
feat(edit): style engine sends every setting the installed plugin app…
bmachek Sep 29, 2026
fe4ff59
feat(plugin): experiment E4f - apply a plugin preset, then delete its…
bmachek Sep 29, 2026
9591356
feat(develop): XMP reader for crs: develop settings, with local sweeps
bmachek Sep 29, 2026
1e94707
Merge branch 'main' into claude/ai-edit-xmp-presets-1a845a
claude Oct 1, 2026
8025462
Merge branch 'claude/ai-edit-xmp-presets-1a845a' into claude/develop-…
claude Oct 1, 2026
2319c89
Merge branch 'claude/develop-registry-pr0' into claude/develop-model-1a
claude Oct 1, 2026
ebaf875
Merge branch 'claude/develop-model-1a' into claude/develop-wb-1d
claude Oct 1, 2026
020c60d
fix(edit): keep a provider note and a training note as separate warnings
claude Oct 1, 2026
b9d07b3
Merge branch 'claude/develop-wb-1d' into claude/develop-style-1f
claude Oct 1, 2026
e8a4dcd
Merge branch 'claude/develop-style-1f' into claude/develop-xmp-1c
claude Oct 1, 2026
8cf154b
feat(develop): XMP preset writer and mask builders
bmachek Oct 2, 2026
fb15d1d
Merge remote-tracking branch 'origin/claude/develop-xmp-1c' into clau…
bmachek Oct 2, 2026
259abde
feat(develop): Lua wire writer with provisional options (draft)
bmachek Oct 2, 2026
8243f2c
Merge remote-tracking branch 'origin/main' into claude/ai-edit-xmp-pr…
bmachek Oct 3, 2026
199c6d8
Merge remote-tracking branch 'origin/claude/ai-edit-xmp-presets-1a845…
bmachek Oct 3, 2026
866d1c6
Merge remote-tracking branch 'origin/claude/develop-registry-pr0' int…
bmachek Oct 3, 2026
4512035
Merge remote-tracking branch 'origin/claude/develop-model-1a' into cl…
bmachek Oct 3, 2026
91ae047
Merge remote-tracking branch 'origin/claude/develop-wb-1d' into claud…
bmachek Oct 3, 2026
14e246a
Merge remote-tracking branch 'origin/claude/develop-style-1f' into cl…
bmachek Oct 3, 2026
0c84cab
Merge remote-tracking branch 'origin/claude/develop-xmp-1c' into clau…
bmachek Oct 3, 2026
369b41e
feat(develop): fold the first experiment run into the Lua writer and …
bmachek Oct 3, 2026
2a029f6
Merge remote-tracking branch 'origin/claude/develop-xmpw-1b' into cla…
bmachek Oct 3, 2026
1ac7257
fix(ai-edit): virtual copies of a virtual copy no longer fail
bmachek Oct 3, 2026
16edd39
Merge remote-tracking branch 'origin/main' into claude/develop-model-1a
bmachek Oct 23, 2026
9d27093
Merge remote-tracking branch 'origin/main' into claude/develop-wb-1d
bmachek Oct 23, 2026
3e801db
Merge branch 'claude/develop-model-1a' into claude/develop-wb-1d
bmachek Oct 23, 2026
614c0d3
Merge branch 'claude/develop-wb-1d' into claude/develop-style-1f
bmachek Oct 23, 2026
e9ef3e3
Merge branch 'claude/develop-style-1f' into claude/develop-xmp-1c
bmachek Oct 23, 2026
2c5b2de
Merge branch 'claude/develop-xmp-1c' into claude/develop-xmpw-1b
bmachek Oct 23, 2026
ca63f02
Merge branch 'claude/develop-xmpw-1b' into claude/develop-luaw-1g
bmachek Oct 23, 2026
8148dd2
Merge remote-tracking branch 'origin/main' into feature/ai-edit-xmp
bmachek Oct 23, 2026
a63e17d
Merge branch 'claude/fix-ai-edit-virtual-copy-of-copy' into feature/a…
bmachek Oct 23, 2026
1368e9a
develop: record raw/Look experiment results, make look_form Full
bmachek Oct 23, 2026
a3726ea
Merge feature/ai-edit-xmp: the native develop model, for AI Edit's LL…
bmachek Nov 13, 2026
1f46a97
AI Edit: an AI retoucher that looks at Lightroom's render and correct…
bmachek Nov 13, 2026
930b7f2
AI Edit retoucher: edits build on the photographer's style; renders s…
bmachek Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# lrg-develop's fixtures and goldens are compared byte for byte by tests (the
# registry snapshot, the XMP writer goldens in written/, the Lua wire goldens
# in wire/); keep them LF on every
# checkout, Windows runners with core.autocrlf=true included. Only this
# folder: server-rs/testdata also holds binary files.
server-rs/testdata/develop/** text eol=lf
6 changes: 6 additions & 0 deletions .github/workflows/golden-tests-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ name: "Golden tests (all model assets)"
# family — AuraFace (261 MB) from Hugging Face, the rest from releases — and
# runs with LRG_REQUIRE_GOLDENS=all, so a missing asset fails instead of
# skipping: the numerical checks either run or the job goes red.
# `all` leaves out the gate's LOCAL_ONLY families (crates/lrg-ml/tests/common),
# whose data is private or not redistributable and can never be fetched here:
# lrg-develop's local sweeps `training-dump` (the maintainer's training rows),
# `lrc-presets` (Lightroom's bundled presets and profiles), `acr-presets`
# (Camera Raw's user folder: its presets and profiles) and `xmp-corpus`
# (a private sidecar corpus).
#
# Deliberately *not* cached. A fresh download each night also verifies that
# the published release assets are still present and fetchable, which is what
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/lint-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ jobs:
set -o pipefail
python3 scripts/check-docs.py 2>&1 | tee /tmp/check-docs.txt
continue-on-error: false
- name: Develop fixtures carry no private data
# server-rs/testdata/develop is derived from the maintainer's own
# Lightroom edits; the check is stdlib-only Python.
run: |
python3 server-rs/scripts/develop_registry/extract_fixtures.py --check-only \
--out-dir server-rs/testdata/develop/lua --root server-rs/testdata/develop
python3 server-rs/scripts/develop_registry/extract_fixtures.py --self-test \
--out-dir server-rs/testdata/develop/lua --root server-rs/testdata/develop
- name: Report possibly-stale pages
if: always()
run: |
Expand Down Expand Up @@ -92,6 +100,13 @@ jobs:
- name: Linting (Rust)
working-directory: server-rs
run: cargo clippy --workspace --all-targets -- -D warnings
# The invariant also guards plugin/LrGeniusAI.lrdevplugin/DevelopEditManager.lua,
# and server-rs-tests.yml runs only for `server-rs/**` changes, so a
# plugin-only PR that reintroduced a `Temp` key would pass there and
# fail on the next unrelated server-rs PR. This job is unfiltered.
- name: No Temp develop key (Rust + DevelopEditManager.lua)
working-directory: server-rs
run: cargo test -p lrg-develop --test no_temp_key

# `llamacpp` is off by default, but lrg-llama is a workspace member, so
# the step above already compiles llama.cpp from source and lints that
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/lua-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ on:
- ".busted"
- ".luacheckrc"
- ".github/workflows/lua-tests.yml"
# The backend's wire goldens: plugin/spec/native_wire_format_spec.lua
# decodes them, so a re-bless must re-run busted.
- "server-rs/testdata/develop/wire/**"
push:
branches: [main]
paths:
- "plugin/**"
- ".busted"
- ".luacheckrc"
- ".github/workflows/lua-tests.yml"
# The backend's wire goldens: plugin/spec/native_wire_format_spec.lua
# decodes them, so a re-bless must re-run busted.
- "server-rs/testdata/develop/wire/**"
# A newer commit on the same ref makes the in-flight run obsolete; main is
# exempt so each commit that lands keeps its own result.
concurrency:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ model_scripts/**/*.bin
**/._.DS_Store
**/.DS_Store
build/
# Source, not build output: lrg-develop's mask builders (`lrg_develop::build`).
!server-rs/crates/lrg-develop/src/build/
dist/
models/
tests/results/*.json
Expand Down
2 changes: 2 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ std = "lua51"
globals = {
"import",
"AestheticRefine",
"EditLoop",
"AgentBridge",
"AgentCommands",
"PluginTests",
Expand Down Expand Up @@ -35,6 +36,7 @@ globals = {
"SkipPhotoContextDialog",
"SpeciesLinks",
"DevelopEditManager",
"DevelopExperiments",
"TaskUpdate"
}

Expand Down
18 changes: 18 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,21 @@ repos:
# checks read; staleness warnings surface in CI.
files: ^(server-rs/crates/lrg-api/src/routes/|plugin/LrGeniusAI\.lrdevplugin/APISearchIndex\.lua|docs/|scripts/check-docs\.py$|CLAUDE\.md$|AGENTS\.md$)
pass_filenames: false
- id: develop-fixture-hygiene
name: develop fixtures carry no private data
# The fixtures under server-rs/testdata/develop are derived from the
# maintainer's own edits and the repository is public. Stdlib-only, so
# plain python3 is enough (no uv).
entry: python3 server-rs/scripts/develop_registry/extract_fixtures.py --check-only --out-dir server-rs/testdata/develop/lua --root server-rs/testdata/develop
language: system
files: ^server-rs/(testdata/develop/|scripts/develop_registry/(extract_fixtures|outpath)\.py$)
pass_filenames: false
- id: develop-fixture-hygiene-self-test
name: develop fixture hygiene check flags what it should
# Crafted files on a copy of the tree; the same cases as
# crates/lrg-develop/tests/fixture_hygiene.rs, so the Python and Rust
# checks cannot drift apart unnoticed.
entry: python3 server-rs/scripts/develop_registry/extract_fixtures.py --self-test --out-dir server-rs/testdata/develop/lua --root server-rs/testdata/develop
language: system
files: ^server-rs/(testdata/develop/|scripts/develop_registry/(extract_fixtures|outpath)\.py$)
pass_filenames: false
17 changes: 13 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,15 @@ failure — this is what stops CI being green on numerical checks that never ran
PR CI provisions the face pair (89 MB) and requires it; the nightly
`golden-tests-full.yml` fetches every family. If you add a golden test, gate it
through `crates/lrg-ml/tests/common/assets_ready` rather than an early
`return`.
`return`. Families whose data can never be in CI (private or not
redistributable) are listed in the gate's `LOCAL_ONLY`: `all` leaves them out,
only naming one explicitly makes its absence a failure. They are `lrg-develop`'s
local sweeps, each reading a path from its variable: `training-dump`
(`LRG_DEVELOP_TRAINING_DUMP`, the maintainer's training rows), `lrc-presets`
(`LRG_LRC_PRESETS_DIR`, Lightroom's bundled presets and profiles),
`acr-presets` (`LRG_ACR_PRESETS_DIR`, Camera Raw's user folder: its presets and
profiles) and `xmp-corpus` (`LRG_XMP_CORPUS_DIR`, a private sidecar corpus); see
`docs/wiki/Dev-Develop-Model.md`.

### Plugin — load into Lightroom

Expand Down Expand Up @@ -157,12 +165,13 @@ Cargo workspace, one binary (`geniusai-server`) across these crates:

- `lrg-common` — config/CLI, error→envelope, version (baked in via `LRG_BACKEND_*` build-time env vars, `option_env!`), logging+rotation, PID/OK lifecycle handshake.
- `lrg-store` — LanceDB wrapper, Arrow schemas, `db_path` bind state machine, backup, stats. `lrg-chroma-reader` is migration-only: reads an old ChromaDB directory (WAL + HNSW segment binaries) left over from the retired Python backend, to migrate its data into LanceDB — no Chroma dependency at runtime.
- `lrg-imaging` — image conversion, EXIF/IPTC/XMP, pHash, culling metrics.
- `lrg-imaging` — image conversion, EXIF/IPTC/XMP, pHash, culling metrics, a render's tones in numbers for AI Edit (`tone.rs`).
- `lrg-ml` — ONNX Runtime (`ort` crate) session management, SigLIP2 pre/post-processing + `tokenizers`-crate Gemma tokenizer, YuNet face detection + FaceNet embedding, MediaPipe Face Mesh V2 + Blendshape V2 for eye state, smile and gaze (`face_state.rs`). Model file locations resolved in `model_paths.rs` (env vars, see [server-rs/README.md](server-rs/README.md)).
- `lrg-analysis` — clustering, person matching, group/cull grading, style engine, keyword clustering.
- `lrg-providers` — LLM provider trait + REST clients (OpenAI, Gemini, Anthropic — Claude through Anthropic's own Messages API in `anthropic.rs`, not its OpenAI-compatible endpoint, which lists no models and ignores the answer format — Ollama, OpenAI-compatible servers — LM Studio found on this computer, and the user's own "Other AI server" such as OpenRouter, llama.cpp `llama-server` or LiteLLM, both in `openai_compatible.rs`), edit-recipe schemas. `local_provider.rs` serves *both* local backends off one `LocalEngine` trait; it has no llama.cpp or MLX dependency of its own.
- `lrg-develop` — the native Lightroom develop model, a leaf crate (no image, network or async dependencies, so `cargo test -p lrg-develop` takes seconds): the key registry (`registry/table.rs`, one `KeySpec` per `crs:` key with type, stored range, UI scale, number format, defaults, policy class, frame scope, file kind), the typed model (`DevelopSettings`, corrections and masks, `Look`, white balance, the policy filter), the readers for `getDevelopSettings()` blobs (`lua::read`) and XMP sidecars, presets and profiles (`xmp::read`, on `roxmltree`), which share their typing and classification rules so both land in the same model, the deterministic develop-preset writer (`xmp::write`), the Lua writer for `applyDevelopSettings` (`lua::write`; provisional until experiments E1/E2/E4/E11, its open choices in `LuaOptions::PROVISIONAL`) — both writers' lossless test modes exist only behind the crate's `test-roundtrip` feature, which only its own dev-dependency enables — and the correction builders (`build`: semantic, gradient and luminance-range masks with SHA-256-derived sync ids). The LLM edit-recipe schema stays in `lrg-providers`, learning/blending in `lrg-analysis::style_engine`. See [Dev-Develop-Model.md](docs/wiki/Dev-Develop-Model.md).
- `lrg-mlx` — supervises the `lrgenius-mlx` Swift sidecar (`native/mlx-sidecar/`) and speaks its JSON-lines stdio protocol. No native build step, no cargo feature; Apple silicon only at runtime.
- `lrg-api` — axum routers (one module per API domain under `routes/`), `db_path` auto-bind middleware, jobs registry, and the browser UI: the pages under `src/ui/` served by `routes/ui.rs`, plus the `ui_bridge` queue that hands their actions to the plugin, and the `agent_bridge` that hands MCP tool calls to the plugin (`/v1/agent/*`).
- `lrg-api` — axum routers (one module per API domain under `routes/`), `db_path` auto-bind middleware, jobs registry, and the browser UI: the pages under `src/ui/` served by `routes/ui.rs`, plus the `ui_bridge` queue that hands their actions to the plugin, the `agent_bridge` that hands MCP tool calls to the plugin (`/v1/agent/*`), and AI Edit's retoucher sessions (`edit_loop.rs`, `/v1/edit/sessions`): a vision model edits through `lrg_providers::chat::ChatModel` with the tools in `lrg_providers::edit_loop`, each edit becomes an `applyDevelopSettings` table through `lrg_develop::build::recipe` and the Lua writer, and the plugin's `EditLoop.lua` applies, renders and hands back each round.
- `lrg-mcp` — the MCP endpoint (`/mcp`, bearer agent token required) AI agents connect to: tools over the index (calling the REST routes in-process) and over Lightroom (through `agent_bridge` to the plugin's `AgentBridge.lua`/`AgentCommands.lua`, which starts with Lightroom), result sets, prompts, and `geniusai-server mcp`, a stdio proxy for Claude Desktop. See [Dev-Agent-Bridge.md](docs/wiki/Dev-Agent-Bridge.md).
- `lrg-chat` — the in-app agent chat (`/v1/chat/*`): a local or cloud model works the catalog through the same tools, as an in-memory MCP client of `lrg-mcp`, with the user's approval (`permissions.rs`). The model answers through `lrg_providers::chat::ChatModel`: `cloud_chat.rs` uses each API's own tool calling, and `local_chat.rs` does it by decoding against a JSON Schema, since llama.cpp cannot render the Jinja templates that carry a model's native tool-call format. See [Dev-Agent-Chat.md](docs/wiki/Dev-Agent-Chat.md).
- `lrg-server` — the binary: CLI (`clap`), lifecycle, self-updater (`routes::update`), `migrate` and `mcp` (stdio proxy) subcommands; merges the `lrg-mcp` and `lrg-chat` routers next to `lrg_api::build_router`'s.
Expand Down Expand Up @@ -194,7 +203,7 @@ Cargo workspace, one binary (`geniusai-server`) across these crates:

### Rust / Backend (`server-rs/`)

- Routes in `lrg-api::routes` (one axum `Router` per domain, merged in `lrg_api::build_router`); business logic in `lrg-analysis`/`lrg-imaging`/`lrg-ml`; LLM/cloud clients in `lrg-providers`.
- Routes in `lrg-api::routes` (one axum `Router` per domain, merged in `lrg_api::build_router`); business logic in `lrg-analysis`/`lrg-imaging`/`lrg-ml`; LLM/cloud clients in `lrg-providers`; the native Lightroom develop model (key registry, typed model, readers and writers of the Lua and XMP forms) in `lrg-develop`, while the LLM edit-recipe schema stays in `lrg-providers` — a new develop key is a registry row in `lrg-develop`, not a field or a string literal elsewhere.
- Always use the `log` facade (`log::info!`/`warn!`/`error!`).
- Manage dependencies via `cargo add`/`cargo remove` (updates `Cargo.toml` + `Cargo.lock`); commit both.
- Code must pass `cargo fmt` and `cargo clippy --workspace --all-targets` with zero warnings before considering a change done.
Expand Down
Loading
Loading