cmux-layout-reducer-ffi: bookmarks_apply, the bookmark op reducer for the app (V-11, step A)
Step A of a two-step FFI move (layer-ownership.md V-11, bead cx-e59e). The app keeps a hand copy of the daemon's bookmark rules (CmuxNextBookmarks/Model/BookmarkTree+Apply.swift) to show an edit before the daemon's echo. #31818 put those rules in cmux-bookmarks-core (the daemon uses it, with a parity test against the daemon's SQL ops). This PR exposes the crate's reducer to the app FFI. There is no Swift change.
Check the app FFI pin: expected, step A of a two-step FFI move. It clears with the re-pin PR that app-ffi-release.yml opens after this lands. Step B (after that re-pin is on feat) makes the app keep a confirmed tree from daemon listings plus a pending-edit log, compute the visible tree through this op, and delete BookmarkTree+Apply.swift, with its Mac suites.
What changes
cmux_layout_reducer_jsonopbookmarks_apply:{browser_profile_id, tree: [Bookmark], ops: [BookmarkOp], now_ms, seed?}->{tree, results: [{result, changed} | {error_code, reason, message}]}(cmux_bookmarks_core::preview::respond). The ops use the daemon's op JSON. A pending op the visible tree refuses changes nothing and reports why, so an edit that the echo already holds does not move the view.reasonnames the rule, so the app keeps its localized refusal texts.cmux-layout-reducer-ffidepends oncmux-bookmarks-corewithout itsurlfeature: the app links no URL parser (nourl/idna/ICU, about 30 packages, incmux-app-ffi/Cargo.lock). Without it,validate_urlchecks the scheme shape, which accepts every URL the parser accepts in the crate's test corpus; so the app never refuses an edit the daemon takes, and one the daemon refuses comes back as the command's error.- Wiring: the
cmux-app-ffi/Cargo.lockentry (written by hand in cargo's format; the cmux-tui.yml stepcmux-layout-reducer-ffi (own workspace)checks it withcargo metadata+git diff --exit-code Cargo.lock), the release push path inapp-ffi-release.yml(required byscripts/cmux-next/tests/app-ffi-release-paths.test.shfor every FFI input crate), and the app crate license gate's first-party GPL list.
Risk: high only because of the one push-path line in app-ffi-release.yml (a release workflow). The product change is one new op nothing calls yet.
Known gaps between the crate's Tree and the daemon (in tree.rs): it cannot check that a profile exists in browser_profiles or that an id is free in every profile, and it does not check a folder icon's syntax or stored asset. A daemon refusal of these reaches the app as the command's error.
Tests
cmux-layout-reducer-ffitests/bookmarks_apply.rs: two pending edits on a confirmed tree both show, a refused third reports{error_code, reason, message}; a malformed request isERR_INVALID.- The reducer itself is proved by #31818's tests (fleet step 17a8481bae447211ebc8f64b: 44 run, 44 passed, including
parity::the_daemon_and_the_shared_reducer_give_the_same_tree_result_and_refusal).
No red step: the op did not exist on the base.
Observed / Not run
cmux-layout-reducer-ffi is its own Cargo workspace, so the fleet runs its tests on never-merge proof refs that build it inside the cmux-tui workspace (listed in stopped.txt):
rust-migration/bookmarks-step-a-ffiproofat f3182416a23fac726e8bf87e8706453c980228b7 = this PR's head 0e562a7db56ed0c62d46f4121326941b30a956cd + one PROOF ONLY commit (workspace member,[workspace]dropped, lock entry).rust-migration/bookmarks-step-a-ffiproof-redat b4b597c55a6fc648ee26fcc7f47c227704fe90f5 = the proof ref without thebookmarks_applyarm.
Command: scripts/ci/cmux-tui-rust-check.sh test bookmarks_apply (linux, through proof-step.py).
- Red at b4b597c55a6f: step 140374ec848488eee8294e83, failed on its own check:
bookmarks_apply::two_quick_edits_both_show_on_the_confirmed_treeFAIL (the op is unknown, so the call returnsERR_INVALID), also alone on the rerun;a_malformed_request_is_invalidpasses there too (an unknown op is alsoERR_INVALID). - Green at f3182416a23f: step 0b07137f01046993fd688beb, passed (2 tests run, 2 passed).
- Clippy at f3182416a23f (the FFI crate inside the workspace): step 9cd3870ed526af8b6ba6f1e2, passed.
- The head is now a4eb6e66ee9390b141357487eecaec0dee60f430: cmux-bot merged feat-cmux-next, which brought two other moves' FFI changes (#31925 sidebar face, #31934 agent cursor visibility). The merge keeps the
bookmarks_applyarm, the lock entries and the release path line;app-ffi-release-paths.test.shok at this head. - Panic ratchet at the exact head a4eb6e66ee93: step 5ab614a9940ba072525b0b29, pass (83 crates checked, 33 at deny).
cmux-layout-reducer-ffiis its own Cargo workspace, so the ratchet's workspace run does not see it; the non-test lines this PR adds there are one match arm withmap_err(..)?only (no indexing, unwrap, expect, panic, unreachable, todo or unimplemented).
Local (Python only): app-ffi-release-paths.test.sh ok; python3 -m unittest tests.test_app_crate_licenses ok; tests.test_cmux_next_route -k app_ffi ok; scripts/cmux-next/prepush-gate.py no new violations.
Not run: the cmux-app-ffi/Cargo.lock check (cargo metadata + git diff --exit-code Cargo.lock); the entry was written by hand in cargo's format. No Swift compile (no Swift change). No pre-merge FFI release exists, so the app half is not built against this op yet.
Changelog
none