cmux-app-ffi links the host budget C ABI; dispatch pressure flags read worst first (step A of the host pressure FFI move)

LA
4d ago
+282-38

Step A of the two-step FFI move of the app's host pressure copy (backlog A10). The host CPU/RAM budget crate from #31799 (cmux-host-budget, which acpmux's daemon already uses) is linked into cmux-app-ffi, so the next CCmuxAppFFI release carries its C ABI. No Swift change and no behavior change: nothing in the app calls the new symbols until step B.

Check the app FFI pin: expected, step A of a two-step FFI move.

Takes back #28790: #28790 reverted one entry of headers_in in scripts/cmux-next/build-app-ffi.sh (the chat index mirror header, which had landed without its release and pin). This PR does not add that entry back: it adds a different header, cmux-host-budget/include/cmux_host_budget_ffi.h, as the next entry of the same list, and lands it by the two-step FFI order (step A here, then the bot's release and re-pin, then step B), which is the order #28790 asked for.

What changes

  • cmux-app-ffi depends on cmux-host-budget with default-features = false (no tokio; its Cargo.lock gains no tokio) and re-exports its C ABI as host_budget, as it does the palette ranker.
  • scripts/cmux-next/build-app-ffi.sh ships cmux-tui/crates/cmux-host-budget/include/cmux_host_budget_ffi.h as the module CCmuxHostBudgetFFI; its cmux_* functions join the export list.
  • .github/workflows/app-ffi-release.yml: cmux-tui/crates/cmux-host-budget/** joins the push paths. The crate is now an FFI input (check-app-ffi-pin.sh --list-inputs lists it), and without the path a change to it would publish no release. scripts/cmux-next/tests/app-ffi-release-paths.test.sh passes. This is the only workflow change; no step, job or trigger changes.
  • The fix the #31799 review asked for before any caller: cmux_host_budget_with_pressure_json now takes the dispatch source's DISPATCH_MEMORYPRESSURE_* flags as they come and reads them worst first (4 critical, then 2 warn, then 1 normal, else unknown), as the Swift monitor (HostPressureMonitor.swift) does. One difference from Swift stays: an event with none of the three flags reads unknown here and .normal in Swift. That case cannot happen, because the source's event mask is [.normal, .warning, .critical]; step B lists it with the other differences. Before, it read one exact level, so a combined event of 6 read "unknown" and dropped the memory reason. The sysctl mapping in evaluate (pressure_word) is unchanged, so acpmux's verdicts are the same.
  • No dirs in the crate: dirs brings option-ext (MPL-2.0) into the app library, and the app crate license gate does not allow MPL-2.0. src/home.rs keeps the rule of dirs::home_dir (dirs-sys 0.5): first $HOME when it is set and not empty; else the passwd entry of the real user (getuid, getpwuid_r, a buffer of _SC_GETPW_R_SIZE_MAX bytes or 512, no retry); else none. Android, iOS and Emscripten read no passwd entry. home_tests.rs tests a set HOME, an unset HOME and an empty HOME. cmux-app-ffi/Cargo.lock loses dirs, dirs-sys, option-ext and the crates they brought in.
  • scripts/cmux-next/check_app_crate_licenses.py names cmux-host-budget as a first-party GPL crate of the app closure, so the real gate does not report it. The gate also reports cmux-motion. That is a base finding, and the follow-up PR fixes it.
  • The async meter tests are behind the tokio feature, so the crate's tests also build without default features (the configuration cmux-app-ffi links).

Step B, after the re-pin is on feat: CmuxNextHostPressure calls this ABI and its Swift formula, sampler, verdict init, with(pressure:) and tracker are deleted; Swift keeps the dispatch source, the 15 s timer, the daemon read, the last verdict and the views. Step B lists the verdict differences between the Swift copy and the daemon's code.

Risk: high (touches app-ffi-release.yml). Lands on an independent Review (<reviewer>): SHIP at <head> receipt.

Changelog

none

Observed / Not run

Observed at the head 515c0b9fd4b. This is my plain merge of feat-cmux-next. It brings in #31927 (the hover C ABI in cmux-app-ffi) and #32374 (the host pressure warning is off by default, with a palette toggle). The only conflict was the module doc in cmux-app-ffi/src/lib.rs, and I kept both sides. The Swift files are the same as on feat:

  • Panic ratchet 34d49d140856819bc09dfb75 and clippy 47247376426894db2734ca75: PENDING.

At 12232b62be9 these passed: panic ceedc091d22484e704d9e818 and clippy 50ecb9b70b3061cb96ea16c4. The PR comment issuecomment-6581650029 classifies the two red checks at that head: "Check the app FFI pin" is the expected step A red, and the daemon test is a load timeout in SnapshotSessionBarrierTests.

Observed at the head 12232b62be9. This is my plain merge of feat-cmux-next. It brings in #32282, the fix for the read_paths.rs clippy base red, and cmux-cloud-start-core, which joins the app FFI:

  • Panic ratchet ceedc091d22484e704d9e818 and clippy 50ecb9b70b3061cb96ea16c4: PENDING.
  • Lockfiles: the cmux-tui/crates/cmux-app-ffi PR check at this head and the fleet clippy, both with --locked.

At bf3bd79fce4 (cmux-bot's merge; an independent SHIP was given there), clippy 6ca0c072d77b9feff4d3504a was red only on the base red in cmux-tui-core/src/mux/tests/read_paths.rs (lines 201, 244, 249 and 237). #32177 added it, and #32282 fixes it. The PR comment classifies it.

Observed at 3aa5af98962 (the PR's own last commit; an independent SHIP was given there):

  • test home::tests: passed on Linux (1e06ba3c0988e5a9ae3f5385) and on macOS (109cb7f6af94f5b91f2f255a).
  • Panic ratchet: head 31b7cb94e3a9cf591fae2af3, base 7ea60b1b546 cbda8d3d961010a620fcec9a. Both show the same counts, cmux-tui indexing_slicing 835 > 831 and string_slice 84 > 82. That is the base red from #28950. The PR has 0 own hits.
  • clippy b2bb2529555cafb701f3bbce: red only on cmux-tui-core output_store/snapshot.rs:537 too_many_arguments. That is a base red that #32092 fixes. The PR has 0 own hits.

Observed earlier (fleet, Linux), for the change that has not changed since:

  • Red at 987967aa63d (the new flag tests, still on the old exact-level mapping), test json::tests, job fc6ead8a4bb87f71e43b134f: the two new tests fail (the_dispatch_flags_read_worst_first, a_combined_critical_event_keeps_the_memory_reason). The other 7 pass.
  • Green at 9e29d1c4f7a (the PR's own last commit; bda755aefa9 only merges feat into it): the whole cmux-tui workspace test, job b55e0534d1695a1e226a711a. All cmux-host-budget and cmux-app-ffi tests pass. 8 tests fail, and each also failed when the rerun ran it alone. None of them is in a file that this PR changes:
    • 3 terminal_host_recovery::host_death::kill_all_restore tests: a Linux base red from #31643, which the REOPEN-F3 owner has.
    • cmux-tui-core connection_handler_owns_every_router_connection_operation: a base red from #30774. #31939 fixes it.
    • 4 that also fail alone at the base 64b25664427:
      • acpmux every_test_binary_runs_in_the_hermetic_environment (5472a06e7265dc6ae1ebaa3d; from #31593)
      • cmux-tui share_chat_waits_for_the_owners_answer (b973957eb8a382f63c3d57c0; from #30550)
      • cmux-tui agent_message_launch_credential (fae49a161128474d850f2ae9) and agent_message_reply_in_place (e67e80ddb524176ae2196fba): both are a tokio worker stack overflow (SIGABRT) at the base too.
  • Panic ratchet at 9e29d1c4f7a, job 4933a9b07161ec03e933c9f3, and at the head bda755aefa9, job 55fb7ac3fa1b825aeb6f500f: both passed. This PR adds no unwrap, expect, panic or indexing in production code.
  • fmt at 9e29d1c4f7a, job 4c44dee9b4a6c1cafe72e284: passed.
  • clippy at the head bda755aefa9, job dffc83f5f08aa4b8f58d36d8: passed. At 9e29d1c4f7a (5a9189ff5de8a62034f38b8e), clippy was red only on the base red in cmux-tui-core agent_chat_notices.rs; #31907 fixed it, and the bot's merge brought that fix in.
  • Lockfiles: both were written on an agent VM with cargo metadata --offline. The proof that they resolve is in jobs that ran cargo with --locked at the head bf3bd79fce4:
    • cmux-app-ffi/Cargo.lock: the PR check cmux-tui/crates/cmux-app-ffi (workflow "rust workspaces", run 41442794928). It ran cargo clippy --locked --workspace --all-targets -- -D warnings and cargo test --locked --workspace in cmux-app-ffi, and passed.
    • cmux-tui/Cargo.lock: the fleet clippy 6ca0c072d77b9feff4d3504a, which runs cargo clippy --workspace --locked.
  • swift-logic-lines.py: swift_logic_lines is 415771 at the head 9e29d1c4f7a and 415771 at the base 49aee507d95; mirror_lines is 70597 at both. There is no Swift change. Step B lowers them.

Review: independent SHIP at bda755aefa9 (the bot's merge of feat a7f48bb820b). cmux-bot merged feat again at 09:56Z (head 5937437a7ed, feat 7ea60b1b546); neither merge changes this PR's files, and the head gets its own review receipt. Its non-blocking notes go to step B so that this head stays the reviewed one: the pressure_word doc still names the dispatch flags, and with --no-default-features meter_tests.rs imports incident but does not use it (a warning, not an error).

The configuration the app links (cmux-host-budget without default features, inside cmux-app-ffi):

  • The PR check cmux-tui/crates/cmux-app-ffi (workflow "rust workspaces", run 41416238787, at bda755aefa9, Linux) ran cargo clippy --locked --workspace --all-targets -- -D warnings and cargo test --locked --workspace in cmux-app-ffi. It compiled cmux-host-budget with default features off, and both passed.
  • macOS compile and the C ABI tests of the crate: test ffi::tests --os macos, job 3a62fddc76e968d8a2aa2ca3 on aws-m4pro-3: passed.

Not run:

  • The release recipe itself (scripts/cmux-next/build-app-ffi.sh: the release slices, the prelink with the export list, xcodebuild -create-xcframework). The fleet refuses it: the script is not on the controller's ci-step allowlist ("invalid ci-step: script is not on the ci-step allowlist"). app-ffi-release.yml runs it after landing. Anything in Swift.