From the third rewrite code review (line references at rewrite 75c79c3). Three related thin-bridge violations; fixing them together moves roughly 60 lines from bridge.rs into UserCommandSender and leaves the shell thin in fact, not just in intent.
1. Queue-add orchestration and domain policy in the shell
Bridge::queue_add_paths (bridge.rs:427-466) expands inputs via the engine scanner, then implements domain policy itself: the per-file rewrite of OutputTarget::SeparateFolder threading source_root through so the output tree mirrors the source tree (bridge.rs:443-451), the OverwriteDecision::FollowSettings default (bridge.rs:452-463), and request assembly. How separate-folder outputs mirror source trees and what overwrite policy a fresh add gets are domain decisions; any second caller (tests, CLI) must copy this glue. Belongs behind one engine call such as UserCommandSender::queue_add_paths(inputs, operation, intent, output_target).
2. QueueItemId allocation is shell-owned domain state
The reducer documents item ids as caller-allocated and unique by contract (reducer.rs:859-868), and the caller is the shell: an AtomicU64 (bridge.rs:275) seeded from every snapshot and bumped past every observed QueueAdded delta (bridge.rs:383-385, :649, :661-663, :720-723). That is a distributed-uniqueness protocol living in the "no domain logic" crate, and it makes the read-model mirror load-bearing for command correctness (a command racing the forwarder's first snapshot absorb can allocate a colliding id and get the whole batch rejected). Inconsistent with the codebase's own precedent: analysis generation ids are reducer-allocated and returned in Reply::AnalysisStarted. Moving allocation into the engine or reducer deletes allocate_item_id, seed_item_ids, and the fetch_max in absorb.
3. Scan extensions read from the shell's mirror instead of the engine's settings
begin_analysis_discovery (bridge.rs:397-401) and queue_add_paths (bridge.rs:435-439) clone stream.model.settings.scan_extensions out of the folded read model and pass it back to the engine. The driver already owns the authoritative Settings; routing them through the IPC mirror means engine behavior depends on shell state, plus a staleness window: a set_settings submitted moments earlier may not be folded yet, so a discovery started right after changing extensions can silently use the old set. The engine should source its own settings.
From the third rewrite code review (line references at
rewrite75c79c3). Three related thin-bridge violations; fixing them together moves roughly 60 lines frombridge.rsintoUserCommandSenderand leaves the shell thin in fact, not just in intent.1. Queue-add orchestration and domain policy in the shell
Bridge::queue_add_paths(bridge.rs:427-466) expands inputs via the engine scanner, then implements domain policy itself: the per-file rewrite ofOutputTarget::SeparateFolderthreadingsource_rootthrough so the output tree mirrors the source tree (bridge.rs:443-451), theOverwriteDecision::FollowSettingsdefault (bridge.rs:452-463), and request assembly. How separate-folder outputs mirror source trees and what overwrite policy a fresh add gets are domain decisions; any second caller (tests, CLI) must copy this glue. Belongs behind one engine call such asUserCommandSender::queue_add_paths(inputs, operation, intent, output_target).2. QueueItemId allocation is shell-owned domain state
The reducer documents item ids as caller-allocated and unique by contract (
reducer.rs:859-868), and the caller is the shell: anAtomicU64(bridge.rs:275) seeded from every snapshot and bumped past every observedQueueAddeddelta (bridge.rs:383-385,:649,:661-663,:720-723). That is a distributed-uniqueness protocol living in the "no domain logic" crate, and it makes the read-model mirror load-bearing for command correctness (a command racing the forwarder's first snapshot absorb can allocate a colliding id and get the whole batch rejected). Inconsistent with the codebase's own precedent: analysis generation ids are reducer-allocated and returned inReply::AnalysisStarted. Moving allocation into the engine or reducer deletesallocate_item_id,seed_item_ids, and thefetch_maxinabsorb.3. Scan extensions read from the shell's mirror instead of the engine's settings
begin_analysis_discovery(bridge.rs:397-401) andqueue_add_paths(bridge.rs:435-439) clonestream.model.settings.scan_extensionsout of the folded read model and pass it back to the engine. The driver already owns the authoritativeSettings; routing them through the IPC mirror means engine behavior depends on shell state, plus a staleness window: aset_settingssubmitted moments earlier may not be folded yet, so a discovery started right after changing extensions can silently use the old set. The engine should source its own settings.