export_progress_and_cancel failed every time on a fast machine (M5 Max, 4
export lanes): the 8 demo photos exported within the 100 ms progress
throttle, so the first progress above 0 was the final 8/8 and the cancel
arrived after the export had finished (a reply to the cancelled request,
or all 8 files on disk).
The test now wraps Headless in a test-only backend that, at the first
photo boundary, keeps polling the server's real progress hook (which
answers pings and reads cancels) until the cancel is read or the test
releases it, bounded at 20 s so a broken cancel fails rather than hangs.
The first export is released once the mid-batch ping is answered (that
was the same race), the second only ends by the cancel. That makes the
outcome exact, so the check tightens from "1..7 files" to "exactly the
one completed photo", which also proves the photos the other lanes had
already rendered are never written. No production code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/mcp (lightcraft-mcp, L5): hand-written MCP over stdio (JSON-RPC 2.0,
protocol 2025-06-18 with 2025-03-26/2024-11-05 negotiation): initialize,
ping, tools/list, tools/call, resources/list/read. Two backends behind one
trait: Headless (in-process engine Session; answers engine.execute,
engine.commands, ui.render, app.export) and Remote (the desktop app's
loopback JSON-lines control channel; lazy connect + reconnect).
Tools: helpers (list_commands, run_command, import, query_photos,
select_photos, list_controls, get/set_develop, apply_preset, crop,
render_photo -> image content, export), UI tools when connected
(screenshot -> image content, inspect_ui, set_ui, list_widgets, click,
press_key, type_text, pointer_gesture), plus one cmd_<id> tool generated
per command in the registry (--compact omits them).
apps/lightcraft-cli: `mcp [--connect [ADDR]] [--demo] [--compact] [FILES]`,
`render <in> -o <out> [--set control=value]... [--settings] [--preset]
[--size] [--quality]`, `commands`, `controls`.
Acceptance e2e: crates/mcp/tests/e2e.rs sets exposure over MCP and renders
(headless and through the TCP transport); apps/lightcraft-cli/tests/cli.rs
does the same through the real binary's stdio. Docs: docs/mcp.md,
docs/control-protocol.md. Also rustfmt of one line in pipeline/finish.rs
(main was not fmt-clean).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>