Skip to content

cmux-app-ffi: generate cmux_motion_ffi.h with cbindgen, fail on drift - #35307

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/cbindgen-ffi-headers
Nov 4, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/cbindgen-ffi-headers

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

include/cmux_motion_ffi.h, the header the Mac app imports as CCmuxMotionFFI, is now generated by cbindgen from the Rust C ABI. A test fails, and prints the diff, when the committed header no longer matches what cbindgen generates.

Until now the header was hand-written next to src/motion.rs and src/hover.rs. If it disagreed with a Rust extern "C" signature or a #[repr(C)] layout, the result was undefined behavior that no Swift test catches. This comes from an ecosystem sweep that looked for mature outside tools to borrow where it's cheap, not to rewrite working code ("We always want to consider the option, consider the alternative."). The hot C-ABI paths stay on C, and iOS keeps UniFFI.

What changed

  • cmux-tui/crates/cmux-app-ffi/cbindgen.toml generates the header from src/motion.rs, src/hover.rs and cmux-motion's hover::c structs. The Rust doc comments become the C comments.
  • tests/c_header.rs regenerates the header and fails with a unified diff when the committed file differs. CMUX_APP_FFI_HEADER_BLESS=1 cargo test --test c_header rewrites it. rust-workspaces.yml already runs cargo test --locked on this workspace whenever the crate or a path dependency (such as cmux-motion) changes, so this adds no CI job.
  • cbindgen is a dev-dependency pinned at =0.29.2 in the committed Cargo.lock. There is no build.rs, so a build never rewrites a tracked file. The staticlib's own dependency versions are unchanged; the lock diff only adds cbindgen's tree and disambiguates syn.
  • The regenerated header is committed. No FFI signature or ABI_VERSION changed.
  • HoverRect::ZERO / ::NULL in cmux-motion get cbindgen:ignore (doc comments only). Without it, cbindgen copies them as CmuxHoverRect_* macros, and NULL's f64_INFINITY is not valid C. The test also fails on any macro other than the include guard, since this ABI has no constants.

Same ABI

  • The old and new headers declare the same 45 types and functions, compared after preprocessing and ignoring order. The only differences are ordering, comments, line wrapping and const double from[4] becoming const double *from (the same parameter type in C).
  • build-app-ffi.sh builds its exported-symbols list by grepping cmux_*( from this header. That grep gives the same 29 symbols for both headers.
  • The header path is unchanged: build-app-ffi.sh (headers_in) still copies cmux-app-ffi/include/cmux_motion_ffi.h into CCmuxMotionFFI.

Other FFI headers (not in this PR)

I tried the same recipe on the other three crates. None reproduce cleanly yet, so each would be its own follow-up:

  • cmux-rd-ffi: cbindgen emits anonymous typedef structs where the hand-written header has tagged ones, and stops on two conflicting NONE constants.
  • cmux-layout-reducer-ffi: its opaque handle types (Focus, Scroll, Mirror, Faces) would need renames in the config.
  • cmux-terminal-client: Option<extern fn> callback typedefs need explicit config.

Testing

Observed

  • cargo test --locked --test c_header: passes on the head.
  • Changing cmux_motion_ease's t: f64 to f32 in src/motion.rs made the test fail with the one-line -double … t / +float … t diff. After reverting, it passes again.
  • Removing the two cbindgen:ignore lines fails the test on unexpected macros in the header. Restoring them passes.
  • The header compiles with clang -fsyntax-only -std=c11 -Wall -Wextra.
  • cargo clippy --locked --all-targets -- -D warnings and cargo fmt --check in cmux-app-ffi are clean. prepush-gate.py reports no new violations.

Not run

  • No app build. Since the header's declarations are unchanged, the CCmuxAppFFI pin keeps its current release. The usual automated re-pin follows, because this changes files under cmux-app-ffi.

Changelog

none

🤖 Generated with Claude Code

Agent session: agent session

…r drift

The Mac app imports include/cmux_motion_ffi.h (CCmuxMotionFFI); it was
hand-written, so a header that disagreed with the Rust extern "C"
signatures or #[repr(C)] layouts was silent UB no Swift test sees.

- cbindgen.toml generates the header from src/motion.rs, src/hover.rs
  and cmux-motion's hover::c structs; the Rust doc comments become the
  C comments.
- tests/c_header.rs regenerates it and fails with a unified diff when
  the committed header differs; CMUX_APP_FFI_HEADER_BLESS=1 rewrites it.
  rust-workspaces.yml already runs cargo test --locked on this workspace
  when it or a path dependency changes, so no new CI job.
- cbindgen is a dev-dependency pinned =0.29.2 in Cargo.lock; no
  build.rs, so no build rewrites a tracked file.

The regenerated header declares the same 45 types and functions as the
hand-written one (compared after preprocessing, order-insensitive) and
yields the same 29 exported symbols build-app-ffi.sh greps from it. Only
order, comments and line wrapping changed. No signature or ABI_VERSION
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-2941a46bf10b2e0f35b7
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 5ce2a76: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr35307 -f ref=lane/cbindgen-ffi-headers -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on fc247cb to compare; do not merge until green; see lane-rules

Shipped in:

@socket-security

socket-security Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​cbindgen@​0.29.2471009310070

View full report

cbindgen copied HoverRect::ZERO and ::NULL as CmuxHoverRect_* macros, and
NULL's f64_INFINITY is not C. Mark both cbindgen:ignore (doc comments
only) and fail the header test on any macro but the include guard. The
header again has exactly the old one's declarations and compiles with
clang -std=c11 -Wall -Wextra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-2941a46bf10b2e0f35b7

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (claude-opus-5-5): SHIP at 5ce2a76

@blacksmith-sh

blacksmith-sh Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Found 11 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp0553bqus/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmprhpyj7uu/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
imported web bundle inputs are not routed:/imported web bundle inputs are not routed: View Logs
nightly cmux-tui resolve/nightly cmux-tui resolve View Logs
nightly.yml must not read vars.NIGHTLY_SIGN while nightly-sign.yml is in shadow mode/
nightly.yml must not read vars.NIGHTLY_SIGN while nightly-sign.yml is in shadow mode
View Logs
test_the_committed_contract_is_the_workflows (main.RollupsFromNeeds.test_the_commit
ted_contract_is_the_workflows)/
test_the_committed_contract_is_the_workflows (main.RollupsFromNeeds.test_the_commit
ted_contract_is_the_workflows)
View Logs

...and 1 more test failures. View all on Blacksmith

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Non-green checks at 5ce2a76, classified before landing:

  • clippy (changed cmux-tui packages): needless_borrow at cmux-tui/src/cloud_environment.rs:22. This PR doesn't touch that file; the same line is on feat-cmux-next. Base red.
  • cmux-next checks: Default cursor (NSCursor+DebugName.swift), Web bundle key inputs and Base-red classifier are in files this PR doesn't touch, so they're base reds. Check the app FFI pin is structural: any FFI source change goes stale until app-ffi-release publishes after merge and the re-pin PR follows (feat(tab-drag): expose index and reveal rules over app FFI #34507 merged with the same red).
  • nightly-guards: the nightly.yml / release-notary contract tests. This PR doesn't touch them, so they're base reds.
  • rust workspaces / cmux-tui/crates/cmux-app-ffi is green and ran tests/c_header.rs (committed_header_is_cbindgen_output ... ok). Risk evidence and merge freeze are green.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at 5ce2a76

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

SHIP at 5ce2a76

Visual verdict: no images; Leo tries the build

Clang confirms identical types and field order across all 45 declarations, with the same 29 export matches; the header passes C11 with warnings as errors. Reviewed drift-test logic and CI wiring; Cargo tests were unavailable because this box lacks Rust tooling.

Follow-ups: none

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves 5ce2a76: an independent review passed this exact head (router-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

This branch was previously deployed

3 inactive deployments
artifacts — 5ce2a76f Deployed Nov 4, 2026 by teamleaderleo via publish tree-addressed cmux-tui #111409
sentry-dsyms — 5ce2a76f Deployed Nov 4, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #111409
ci-routing-agent — 5ce2a76f Deployed Nov 4, 2026 by teamleaderleo via cmux-tui tree preflight #111409
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant