Review round 2 (F1 Important + two Minor):
F1 — per-sleeve exception isolation for the UCITS price fetch. A raise (not
empty return) from the IB API previously propagated out of route_ucits, past
_plan's per-order isolation, aborting the WHOLE rebalance. Now IbkrBroker.
market_price wraps the IB round-trip in try/except -> log + None, and
route_ucits wraps price_fn(listing) in try/except -> log + skip. One thin/broken
sleeve (e.g. the new DBMF LU2951555585 line) can no longer wedge the book.
F2 — a non-injective UCITS map raised inside the broker block and was
misreported as "paper-envelope refused". The CLI now validates the map with
apply_ucits_map BEFORE connecting, printing a clear "UCITS map error: collision".
F3 — first UCITS run compared US-keyed prior positions against UCITS-keyed
prices, booking a spurious multistrat_exec return. Extracted
exec_return_for_run(pos_prior, prices, venue): a venue switch (prior book-state
venue tag != current venue) is booked as INCEPTION (no return); same-venue runs
delegate to compute_exec_return as before.
Tests: +5 (price_fn raises -> skip others route; market_price IB-raise -> None;
venue switch -> inception; same-venue -> normal return; no-prior -> inception).
uv run pytest -q: 1991 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>