fixture contribute: step 2 always fails on Unifying receivers on Linux (DJ reports rejected as malformed)
- Dominant language
- Rust
- Stars
- 21k
- Forks
- 675
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 172
Description
### Summary
`openlogi fixture contribute` step 2 can never succeed for a Unifying receiver on Linux while `hid-logitech-dj` is bound (the default). The receiver's hidraw node carries DJ reports (`0x20`/`0x21`) alongside HID++, `HidppMessage::read_raw` classifies them as `ChannelObservation::MalformedIncomingReport`, and the sanitizer rejects the whole cassette on any such observation.
### Environment
- Arch Linux x86_64, `master` at `e846e6f`, release build
- Unifying receiver `046d:c52b`, bound to `logitech-djreceiver`; `hid_logitech_dj` and `hid_logitech_hidpp` loaded
- Device: MX Ergo (wpid `406f`), slot 1
- The receiver's report descriptor declares report IDs `0x10`, `0x11`, `0x20`, `0x21`
### Reproduce
```
openlogi fixture contribute --id mx-ergo-001 --name "MX Ergo" \
--device "MX Ergo Multi-Device Trackball " --output fixtures/devices/mx-ergo-001
# step 1 (agent running) succeeds; stop the agent, rerun:
recording 1/8: feature-table
channel candidate 1: sanitizer rejected
audit: Unifying receiver identity replaced 1 occurrence(s) with synthetic 4F4C5201
rejection: incoming report was malformed
rejection: incoming report was malformed
Error: no sanitized channel candidate reproduced the captured semantic observation and completed strict replay; no fixture was written
```
Reproduced 4/4 times.
### Evidence
A passive reader on the same hidraw node during step 2, with the trackball untouched:
```
DJ reports by id/type: {'0x20/type0x41': 4, '0x20/type0x02': 443}
first 0x20/type0x41 at t+1.01s: 200141016f401e0000000400000000
first 0x20/type0x02 at t+7.47s (after the capture had already failed — me moving the mouse)
```
So the rejected reports are the DJ "device paired" notifications (`0x20 … 41`) that appear during discovery, not pointer motion. Any pointer motion during a capture would add DJ `0x02` mouse reports on the same node too. The kernel's `hid-logitech-hidpp` also talks to the device on this node (sw id `0x01`).
### Where
- `crates/openlogi-hidpp/src/channel.rs` `read_loop`: any report that fails `HidppMessage::read_raw` becomes `MalformedIncomingReport` — a report with a non-HID++ report ID is treated the same as a truncated HID++ report.
- `crates/openlogi-hid/src/recording/cassette/sanitizer.rs` `unassociated_rejection`: `MalformedIncomingReport` → `CassetteRejectionReason::MalformedIncomingReport`.
The normal channel already drops these reports (`report not HID++ — dropped`), so replay never needs them.
### Possible direction
Separate "another protocol's report on this node" (report ID not `0x10`/`0x11`) from "HID++ report ID with invalid framing", and have the recorder drop the former instead of rejecting. Happy to send a PR if that direction is acceptable, and to retest on this hardware either way.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with read_loop in crates/openlogi-hidpp/src/channel.rs and then inspect unassociated_rejection in crates/openlogi-hid/src/recording/cassette/sanitizer.rs. Compare the normal channel's handling of non-HID++ reports, and verify that fixture contribute can complete for the Unifying receiver without rejecting DJ reports while still rejecting malformed HID++ reports.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, rust
- Domain
- embedded-iot, operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100