ci / bun-nix (pull_request) Successful in 30s
ci / docs-site (pull_request) Successful in 1m16s
ci / web (pull_request) Successful in 1m22s
apple / swift (pull_request) Successful in 1m33s
apple / screenshots (pull_request) Skipped
windows-drivers / driver-build (pull_request) Successful in 1m38s
ci / rust-arm64 (pull_request) Successful in 2m12s
windows-drivers / probe-and-proto (pull_request) Successful in 21s
android / android (pull_request) Successful in 3m39s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m11s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m14s
ci / rust (pull_request) Successful in 6m32s
Reverts half of1eab4b66and closes G10's open frame question, both settled by the same measurement.1eab4b66made two corrections to the Apple phone-gyro mirror. The negation was right and stays: Apple reports the gravity VECTOR, pointing down, while an accelerometer measures proper acceleration, pointing up at rest, and the wire carries the latter. The frame change was wrong, and this removes it. The mistake was a name collision. Two different frames are both called "the controller frame". GCMotion reports a CONTROLLER in (Right, Forward, Up) — measured on a real DualSense — which is not the wire's frame, which is why `GamepadCapture.forwardMotion` converts. The mirror's orientation remap resolves THIS DEVICE into the frame its header describes, x right, y up, z out of the screen. For the pose that mirror exists to serve — a phone clipped upright with the screen facing the player — "out of the screen" points at the player, so that frame is (Right, Up, Backward), which IS the wire's. It was already correct. Applying the controller path's conversion on top rotated it out of true: a phone sitting still would have reported gravity as −1 g on the roll axis rather than +1 g up, i.e. claimed to be lying on its edge. Reasoning by analogy is what produced it — "the mirror says controller frame, the capture path says controller frame, so the same fix applies". Both files say it; they mean different things. What caught it was measuring the Android twin, which does the same thing straight through. On glass: a DualSense on Bluetooth to a phone, streaming to a Linux host, reads +1 g on the up axis end to end. Had the Apple mirror needed a conversion, the Android one would have needed the same one and would have been visibly wrong. It is not. The same run settles G10's frame, which shipped straight-through and explicitly unverified because nobody had put a Bluetooth pad in front of the platform sensor framework. Now somebody has. `PadSensors`' own first-sample log read `accel 0, 10000, 0` — exactly 1 g on slot 1 — and at the far end hid-playstation published gravity as +0.991 g on ABS_Y, with every rotation driving its correctly-named axis and the signs agreeing with gravity's independent witness on 95 of 100 rotating samples. Android hands a controller's sensors over in the pad's own frame, as documented. No remap, and the comment now says measured instead of assumed. Worth recording why the earlier suspicion was wrong, since it is the same trap in the other direction: Android's DEVICE sensor frame really does put +z out of the screen, so a flat phone puts gravity on z — but a CONTROLLER's sensors are reported in the controller's frame, not the phone's. One platform, two conventions, chosen by what the sensor is attached to. Gate: Apple macOS `swift build` + full suite (215 tests, 5 skipped, 0 failures) and the iOS-triple typecheck, which is what actually compiles `DeviceGyro.swift`; Android `:kit:compileDebugKotlin`, `:kit:testDebugUnitTest`, `:app:compileDebugKotlin`. Green. Still owed: `DeviceGyroRemap`'s four orientation matrices remain derived — this run used a controller's own sensors, not the mirror, so it says nothing about them. They need a gyro-less pad on wire index 0 and a phone turned through all four orientations.