agent cursor visibility: Swift calls the Rust rules over the app FFI; the Swift rules are deleted (B9 step B)
Step B of a two-step FFI move (rust-migration backlog B9). The agent cursor's visibility now comes from the Rust rules that #31934 landed. #31974 re-pinned the app to cmux-app-ffi-c94408d3c4853068b32e73e50afd3cbbeea776c7, which carries them. The Swift copy of the rules is deleted. This PR has no Rust change and no pin change.
AgentCursorVisibilityResolver.resolve(target:in:) keeps its signature. It encodes {target, snapshot} and calls cmux_layout_reducer_json("agent_cursor_visibility") through the new raw-bytes FocusReducer.call(_:json:), then decodes the answer into AgentCursorVisibility. The encoder and decoder write non-finite numbers as the strings the Rust wire reads ("inf", "-inf", "nan"). So a snapshot with a converted CGRect.null or an infinite scale keeps the result the Swift rules gave it.
What stays in Swift:
- The value types the App builds and reads.
placement(forWindow:).- The App's snapshot builder, which reads AppKit views.
The anchor's wire name is FFI glue in AgentCursorAnchor+FFI.swift. The debug socket (DebugAgentCursor) uses it instead of its own copy.
Tests
CmuxNextAppTests/AgentCursorVisibilityVectorTests replays schemas/agent-cursor-visibility/vectors.json through the Swift call, plus non-finite cases, the page scale, every anchor name and the old placement tests. It is in an existing target, so no new test target is added.
- The suite's expectations are the old behavior: on the never-merge ref
rust-migration/cursor-visibility-oldswift2(9d8044946c8, this suite with the old Swift resolver restored), step4fc11b4a001e4f251721ef3f(aws-m4pro-7) passed, 7 tests. - At this head, through the Rust call (proof-step.py green): step
74648ca9a57684eca6250a8cpassed, 7 tests in 1 suite, at 60cb287dbe1 (Xcode 26.6). Earlier, at b240e38b11f: step4029334b707cd63cf4b45d5e(aws-m4pro-6) passed.
AgentCursorVisibilityResolveBench (off unless CMUX_AGENT_CURSOR_VISIBILITY_BENCH=1) times one resolve per vector. The A/B runs it alone on an exclusive host, once with the head's resolver and once with the old Swift resolver restored.
Line accounting (swift-logic-lines.py, merge base ba780613748 → this head)
| row | base | head |
|---|---|---|
| swift_logic_lines | 416313 | 416222 (-91) |
| mirror_lines | 70686 | 70712 (+26) |
Swift rules deleted: the resolver's 91 LOGIC lines. Its row in swift-logic-classes.tsv is now PLATFORM, because what is left only encodes, calls and decodes. The new extension AgentCursorAnchor in AgentCursorAnchor+FFI.swift is a hand copy of the Rust Anchor::as_str and parse, so its row is MIRROR (+26), like DaemonCapability. app_lines -10 (DebugAgentCursor).
Changelog
none
Observed / Not run
Observed:
- Compile-check at 440584d687f:
3bcc994ff74702812b1e11a1failed. The newAgentCursorAnchor+FFI.swiftextension took the target's default MainActor isolation, and the resolver's nonisolated decode could not callinit?(wireName:). b240e38b11f makes the extensionnonisolated, like the enum. Compile-check at b240e38b11f:d99951c21ebc1bb900b1e8dbpassed. Compile-check at 60cb287dbe1 (this head):6c63ea2664c88143b26396a8passed. Earlier Swift compiles of the same Swift half:70465d8a5869b43387570aa6passed (1b9e3c972de);e8211715de251bc8ba17f73epassed (old-rules ref). - Old Swift rules under this suite: step
4fc11b4a001e4f251721ef3fpassed (above). - The Rust rules, their red/green tests and the C ABI test are in #31934 (merged).
- Review receipts: SHIP at b240e38b11f (comment 6573462111), SHIP at 60cb287dbe1 (comment 6573857414).
prepush-gate.py: no new violations.
Perf A/B (AgentCursorVisibilityResolveBench, release build, one resolve per vector; a class exclusive measurement step per arm on cmux18-perf). Before = the old Swift resolver restored from feat ba780613748; after = this head. Only rounds whose two arms both ran on cmux18-perf count:
| round | arm | job | p50 | p95 | p99 | max |
|---|---|---|---|---|---|---|
| 1 | before | fadbace5a2fd5ae64c63cdf2 | no result: the release test build crashed the Xcode 26.6 optimizer in CloudStartSessionAsksTests, a base red that #32069 fixes | |||
| 1 | after | d620f0ccd432f5526051b067 | cancelled before it started (the same build would crash) | |||
| 2 | before | (mini scheduler, spec rmcv-b9-ab-before-r2) | PENDING | |||
| 2 | after | (mini scheduler, spec rmcv-b9-ab-after-r2) | PENDING |
Not done (Rust follow-ups, backlog B9b):
- The debug socket's
snapshot_json(DebugAgentCursor.swift) encodes with a plain JSONEncoder, so a snapshot with non-finite numbers gives "". cmux-app-ffibuilds serde_json withoutfloat_roundtrip, so a Swift number with 17 significant digits can parse 1 ULP off in Rust (under 1e-13 pt).- NaN rects: Rust
f64::min/maxskip NaN, while CoreGraphics passes it through. The app builds no NaN rect. - The panic step (
cmux-tui-rust-check.sh panic): not applicable. This PR changes no Rust; the Rust half and its checks are #31934.
Not ready-to-land until the release perf A/B (both arms on one mini) is in this body. It waits for #32069, which fixes the release build.