feat(fixture): add an MX Ergo Unifying fixture - #1374
Chernicharo wants to merge 1 commit into
Conversation
Captured from an original MX Ergo (wpid 406f, fw MPM06.03_B0022) on a Unifying receiver with `openlogi fixture contribute`: eight read-only cassettes plus the semantic profile. The device reports no 0x2201/0x2202 and no 0x2110, so it covers a trackball whose pointer and SmartShift settings are unsupported.
|
| { | ||
| "request_match": "hidpp20", | ||
| "request": "10010210000000", | ||
| "response": "1101021001424f5449000001000000cde885f300", |
There was a problem hiding this comment.
Device Identity Remains Unsanitized
This firmware response, and the response on line 40, retain the captured cd e8 85 f3 00 extra_version. The repository describes this field as device-specific, but the manifest does not declare or synthesize it. Because identity extraction skips DEVICE_INFORMATION function 1, sanitization and strict verification both overlook these bytes, leaving a device-specific hardware value in the committed corpus despite its synthetic-identity guarantee. Remove or deterministically synthesize this field and include it in identity verification.
How this was verified: The response layout places these bytes in extra_version, repository diagnostics describe that field as device-specific, and the fixture identity extractor explicitly skips function 1 responses.
There was a problem hiding this comment.
Confirmed, and it goes deeper than the capture: the field is not declarable in the current ledger design, so I would rather have your call than guess.
What I verified on this unit: getFwInfo responses end in cd e8 85 f3 00 for both entities, and crates/openlogi-device/src/write/diagnostics.rs describes extra_version as device-specific. ProtocolIdentityExtractor validates the function-1 response framing but claims no field in it, so the bytes ship as captured.
I prototyped the obvious fix — a tagged five-byte synthetic identity for the field, claimed at (DEVICE_INFORMATION, 1) — and it runs into two things:
- Self-replay compares the replayed
SemanticObservationwith the capture, andFirmwareEntitiescarriesextra_version, so rewriting it fails the exact match. Workable: compare against the capture with the audited synthetic value substituted, so every other byte still has to match exactly. build_profile_ledgerderives the ledger only from the profile's device model.extra_versionis not in the profile, so verification then rejects the cassette withextra synthetic FirmwareExtraVersion value not declared by the ledger. That is the real blocker, and the two ways out are both design calls:- carry the field in the semantic profile's device model (an IPC wire type, so a protocol bump), or
- let the ledger accept a cassette-derived representation bound to the device principal, which relaxes "the ledger is profile-derived".
Which direction do you want? I have the hardware and the branch, so I can finish either quickly and regenerate this fixture. If you would rather not decide now, I can drop the firmware-entities case from this PR so nothing device-specific ships, and leave the capture gap to a follow-up issue.
There was a problem hiding this comment.
Drop firmware-entities from this PR and leave the ledger/schema change for a follow-up.
A protocol bump to DeviceModelInfo would put a per-firmware-entity field in the device-model profile, while accepting a cassette-derived representation would weaken the profile-derived identity invariant. Keeping the cassette unchanged is not acceptable because extra_version remains captured device-specific data.
Removing this case is the safest scoped fix: the existing profile-derived unit ID and receiver identities remain covered by the other cassettes, and no unsanitized firmware-specific value ships. The follow-up should introduce an explicit semantic firmware-entity representation plus a dedicated synthetic identity kind, then regenerate this cassette and verify self-replay with the audited replacement.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
|
Marked this a draft until the |
Summary
Adds the first captured fixture to the corpus: an original MX Ergo (wpid
406f, firmwareMPM06.03_B0022) on a Unifying receiver, produced withopenlogi fixture contribute. It covers a device shape the synthetic corpus does not: a trackball that reports no0x2201/0x2202and no0x2110, so DPI and SmartShift are legitimately unsupported, while0x1b04exposes wheel tilt and a precision button.Changes
fixtures/devices/mx-ergo-001/: manifest, semantic profile and eight read-only cassettes (feature table, firmware entities, reprogrammable controls, raw battery, DPI info, SmartShift status, wheel mode, backlight state).Testing
openlogi fixture contributecompleted both phases against the hardware; the wizard's own strict verification passed (schema, synthetic identity ledger, profile/case relationships, and replay of all eight cassettes).cargo test -p openlogi-cli fixtureon this branch — 48 passed, includingfixture::verify, which discovers and strictly verifies every directory in the corpus (this branch has no source changes, so the fixture is validated by unmodified verification code)4F4C5201does.Note
Step 2 of the wizard cannot complete on a Unifying receiver on Linux today (#1349): the receiver's node also carries Logitech DJ reports and the recorder rejects the cassette. This capture was taken with the fix in #1373 applied. The fixture itself does not depend on that fix — it verifies against master as shown above — but it cannot be reproduced on this hardware until the fix lands.