Repository navigation
cmux-bookmarks-core: the bookmark op rules as a crate the daemon uses, plus the in-memory reducer for the app (V-11) - #31818
Conversation
…d the app share (V-11, PR 1 of 2)
The rules of bookmarks-v1 (limits, validation, op and record types) move out
of cmux-tui-core workspace_registry/personal_bookmarks.rs into a new pure
crate, cmux-bookmarks-core. The daemon re-exports them and keeps its SQLite
rows, replay ledger and journal; its behavior and the op JSON (the replay
fingerprint) do not change.
The crate adds Tree: one profile's tree in memory with the daemon's op
reducer, step for step (same checks, order, messages, clamping and result
JSON). cmux-layout-reducer-ffi exposes it to the app as the JSON op
`bookmarks_apply` ({browser_profile_id, tree, ops, now_ms, seed?} ->
{tree, results}), so the app can show its pending edits on the confirmed
tree by the daemon's rules and delete its Swift copy
(BookmarkTree+Apply.swift) in PR 2.
The URL parser stays out of the app FFI: the crate's `url` feature (the
daemon turns it on) parses with url::Url; without it the check is the
scheme shape, which accepts every URL the parser accepts.
Tests (run on the fleet; ids in the PR):
- cmux-bookmarks-core tests/bookmarks/tree.rs: the Swift BookmarkTreeTests
(suite deleted in 36f2381) ported with daemon ids and refusals, plus
imports, replace, ids, profiles, icons, op JSON, preview and the URL check.
- cmux-tui-core server/bookmark_parity_tests.rs: 1000 seeded random ops
through the daemon and through Tree, same tree, result and refusal.
- cmux-layout-reducer-ffi tests/bookmarks_apply.rs: the op through the C ABI.
Expected red: Check the app FFI pin (cmux-layout-reducer-ffi now links
cmux-bookmarks-core); the re-pin follows the landing.
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green Shipped in:
|
|
Found 6 test failures on Blacksmith runners: Failures
|
…p's localized text The preview result carries it as `reason`; the daemon's wire is unchanged.
… with the moved records)
… path and lock entry move to the one FFI move PR The FFI rule (cmux-ad 04:35Z): one PR per FFI move with the Rust change, the CCmuxAppFFI re-pin and the Swift call. So this PR changes no FFI input: cmux-layout-reducer-ffi, app-ffi-release.yml, cmux-app-ffi/Cargo.lock and the app crate license list are as on the base. The op is kept on rust-migration/bookmarks-ffi-move.
|
Review (independent-reviewer-rm-bookmarks): SHIP at eedc9e0 Correctness review against layer-ownership.md 1-3, REVIEW-STANDARD.md, REVIEW.md and CLAUDE.md. I read the code; I did not build or run tests on this machine. Claims checked against the code
Non-blocking notes
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
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, and a second quick edit flickers away until its echo (layer-ownership.md V-11, bead cx-e59e). This PR moves the rules into a new pure crate,cmux-bookmarks-core, which the daemon uses now, and adds the in-memory reducer the app will call. It changes no app FFI input. After it lands, step A of a two-step FFI move adds the opbookmarks_applyto the app FFI (branchrust-migration/bookmarks-ffi-move); step B, after the re-pin, makes the app call it and deletesBookmarkTree+Apply.swift.What changes
cmux-tui/crates/cmux-bookmarks-core(no I/O; zero hits of the panic ratchet's deny lints). It holds the limits, the validation, the record and op types (Bookmark,BookmarkOp, ...) andTree: one profile's tree in memory with the daemon's op reducer, step for step (same checks in the same order, same messages, same index clamping, same result JSON; every check runs before the first change, like the daemon's transaction).preview::respondis the request the app will send: a confirmed listing plus pending ops in, the visible tree plus one result per op out. Each refusal also names its rule (Reason), so the app can keep its localized refusal texts.workspace_registry/personal_bookmarks.rsre-exports the moved items and keeps its SQLite rows, replay ledger and journal. The op types serialize as before, so the replay fingerprint of a keyed op does not change. URLs are still parsed withurl::Url(the crate'surlfeature, which cmux-tui-core turns on). Without the feature, the check is the scheme shape (has_url_scheme), which accepts every URL the parser accepts in the test corpus; that is what the app FFI will use, so it links no URL parser.Cargo.lockentry; one comment incmux-tui/scripts/check-sdk-schema.py.Known gaps between
Treeand the daemon (also intree.rs):Treeholds one profile, so it cannot check that a profile exists inbrowser_profiles(it refuses another profile as unknown) or that an id is free in every profile; and it does not check a folder icon's syntax or that its stored asset exists. When the daemon refuses one of these, the app gets the command's error.Follow-up: two Rust implementations of the reducer now exist (the daemon's SQL ops and
Tree), held equal by the parity test. A later change can make the daemon apply each op throughTreeon a loaded working set (the profile's tree, then write the changed rows), if a bench shows that is cheap at 100,000 nodes; then the SQL ops go.Tests
cmux-bookmarks-coretests/bookmarks/tree.rs: the SwiftBookmarkTreeTestsof the hand copy (deleted with the CmuxNext unit suites in 36f2381) ported with the daemon's ids and refusals, plus the daemon rules the Swift copy lacked (import and replace, id and profile checks, icon only on a folder, no favicon key on an import), each refusal'sReason, the op JSON shape, the preview (two pending edits both show; the echo does not move the view), id minting, and the scheme check againsturl::Url::parse.cmux-tui-coreserver/bookmark_parity_tests.rs: 1000 seeded random ops (4 seeds x 250 steps: creates, moves, deletes, updates, icons, imports and replaces, with bad parents, bad URLs, NUL titles, unknown ids). Each step reads the daemon's listing, applies the op to the daemon and toTree, and requires the same tree, the same result and the same refusal code and message.This is a move with no bug to reproduce: the crate does not exist on the base, so there is no red step. The parity test is the proof that the Rust reducer equals the daemon.
Observed / Not run
Observed (fleet):
cmux-tui-rust-check.sh test bookmarkat 43dd4a9: step 17a8481bae447211ebc8f64b, 44 tests run, 44 passed (the 19bookmark_tree::tests of the crate,server::bookmarks::tests::parity::the_daemon_and_the_shared_reducer_give_the_same_tree_result_and_refusal, and the existing daemon bookmark tests). The head eedc9e0 only merges feat-cmux-next on top; the patch is the same.cmux-tui/src/plugin_manager.rs:233suspicious_open_options, a base red fixed on feat by ca404c4; hence the merge of the base.)acpmuxclippy::indexing_slicing697 > baseline 686 (base finding; this PR touches no acpmux file). Nocmux-bookmarks-corehit is listed.BookmarkOutcome, a daemon-only type, was cut with the moved records; fixed in 2473f93). Its fmt step 2d19e4be180c failed only onacpmux/src/hub/list_guard.rsandhub/mod.rs(base red).scripts/cmux-next/prepush-gate.pyno new violations;cmux-tui/scripts/check-sdk-schema.pyok;.github/scripts/rust-workspaces.py checkok.Not run: no Swift compile (no Swift change); no perf A/B (nothing calls the preview yet).
Changelog
none