async fn update_layout_via_reducer(
state: &AppState,
mux_obj_value: Value,
tab_id: String,
new_tree: Option<LayoutNode>,
) -> WebReturnTypeExpand description
SPEC_864 Phase 2 — route a frontend UpdateObject layout push through
the reducer as a single LayoutSetTree dispatch.
Dispatch AND SQLite apply happen under ONE hold of the srv_state
mutex (reagent P1 #1970): with dispatch_to_reducer’s usual
release-before-I/O contract, two concurrent pushes for the same tab
could dispatch in order A→B but persist B→A, leaving db_layout on
the older tree while TabRecord (authoritative as of this route)
holds the newer one — the exact coherence this route exists to
guarantee, and a regression vs. the legacy single atomic update_raw.
Holding the lock across the row UPDATE (a sub-ms local SQLite write)
makes persist order equal dispatch order; the SQLite-failure rollback
re-dispatch also runs inside the same hold so no interleaved dispatch
can observe the un-rolled-back state. Publish happens after release
(the wave-obj bridge re-reads the row, so publish must follow the
write; subscribers never see events out of dispatch order because
publish order still matches — see below).
Not carried: LayoutState.meta. The legacy whole-row write persisted
it incidentally, but nothing mutates layout meta (UpdateObjectMeta
rejects OTYPE_LAYOUT and the frontend’s persistToBackend writes only
tree/focus/magnify/leaforder/pendingbackendactions), so the reducer
route leaves the stored meta untouched.