Tested on real Sony A7R VI brackets (67 MP): the merged radiance agrees with every frame within ~1 % in all three
channels and the measured exposure steps match EXIF within 0.03 EV, and the black-hole stars and the merge memory are
fixed. What keeps the row 🟡 goes to gap 29: AI Denoise doesn't run on merged (linear) DNGs as Lightroom's does, JPEG
brackets have no camera response curve, and every frame is held as 32-bit float (~0.86 GB per 67 MP frame). Merge area
100 % → 88 % in target-app-parity.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photo ▸ Photo Merge ▸ HDR for Each Bracket Set (`merge.hdrBatch`) merges many brackets in one go with the last HDR
options, as Lightroom merges a selection of stacks: one HDR per stack among the photos, then per bracket among the
unstacked ones (`Catalog::bracket_sets`: photos taken less than about a second after the previous one's shutter
closed, at exposures at least ½ EV apart; bursts at one exposure and earlier merge results are left out). The app runs
the merges one after the other in the activity stack (cancelling one stops the rest; a failed set doesn't); the
command runs them inline for the CLI and MCP (`lightcraft-cli merge hdr-batch FILES…`) and reports merged and failed
sets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 7-frame bracket of 67 MP raws ran out of memory: the merge decoded the frames four at a time, and the AHD demosaic
collected every tile before copying them into a second full-size picture. Full-size frames now decode one after the
other (the demosaic is parallel inside; previews stay parallel), and AHD fills the picture by rows of tiles. Peak memory
of 67 MP HDR merges: 3 frames 4.8 → 3.8 GB, 4 frames 6.3 → 4.7 GB, about the same time; AHD output is pixel-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A star moving across a night bracket became a black hole: where the reference was clipped, frames in which the star
had moved read the dark sky and replaced it. Such frames are now left out there (a clipped reference pixel means at
least that bright). Deghost compared ≤ 1024 px versions and, in dark areas, let the brighter frames predict
themselves, so their ghosts looked consistent; it now uses ≤ 2048 px maps (same feather share) and trusts the
reference down to 0.1 % of its clip. Aligned frames are warped one at a time, each replacing its original, instead of
all copies at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wake the serving loop after protocol output fails and preserve the actual writer error. Use one process-owned stdin worker so the CLI can finish normal backend cleanup without waiting for client EOF, and terminate the output worker even when a progress hook is retained.
Closes#1527
Co-Authored-By: OpenAI Codex <noreply@openai.com>
Resolve doc conflicts in gaps, parity-checklist and raw-parity; coverage
numbers regenerated with docs/raw/coverage.py, parity summary refreshed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs/parity-checklist.md: main's header and summary (regenerated) with this PR's Solo Mode note and history row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A raw imported while its format couldn't be decoded kept preview_only until a
manual Reload, so new decoders in a release never reached existing photos.
When a library opens in another app version, its preview-only photos whose
stamp differs are probed again in the background and get exactly what Reload
from Disk gives them (shared reload_ops): a file that decodes now loses the
flag, one that still doesn't gets the current reason and a fresh stamp. The
result is one journaled system batch, not an undo step; develop settings and
history are kept. Unreadable files are left for next time, photos changed
while probing are skipped, and a result for a library closed meanwhile is
dropped. A toast says how many raws now develop from their raw data.
- engine: cmd/recheck.rs (stale / probe / apply) and
library.recheckPreviewOnly {all?} -> {checked, updated, cleared}; refused
without file access, where every probe would clear every flag
- desktop: recheck::tick once per library generation, through tasks::spawn
("Checking raw files" in the activity stack)
- the preview-only notice (Edit, Info) gains a Reload from Disk button
- strings translated in all nine catalogs; docs/mcp.md and the parity row
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Treat a zero-width feather transition as a hard edge so exact-radius pixels remain unchanged, including at zero opacity. Add actual image regressions for all spot modes, image corners and two clone output sizes.
Validation: both new regressions failed on the original implementation and pass here. Formatting, workspace Clippy, HEIF and a generated-photo app smoke test pass. The unfiltered workspace gate remains blocked by the existing all-masks overlay test tracked by #1599; later parity, layers, assets and WASM stages are pending. This is a source checkpoint, not a green release candidate.
Contributor: @eliets2
Docs recounted with docs/raw/coverage.py: 241 verified, 35 unsupported. The MCP
headless extension list now comes from engine::import::EXTENSIONS upstream, which
already carries mef.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts in ROADMAP.md, docs/gaps.md, docs/parity-checklist.md and docs/raw-parity.md resolved by
taking upstream's text and re-applying this PR's rows; coverage re-measured with docs/raw/coverage.py
(334 verified, 844 unverified, 50% of photographers on a verified body).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Coverage regenerated with docs/raw/coverage.py (258 verified, 920 unverified);
doc conflicts resolved on upstream's text with this PR's numbers re-applied.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>