Skip to content

feat: onboard profiles (0x8100) support for G-series mice - #459

Open
Stanley5249 wants to merge 28 commits into
AprilNEA:masterfrom
Stanley5249:feat/onboard-profiles
Open

feat: onboard profiles (0x8100) support for G-series mice#459
Stanley5249 wants to merge 28 commits into
AprilNEA:masterfrom
Stanley5249:feat/onboard-profiles

Conversation

@Stanley5249

@Stanley5249 Stanley5249 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for HID++ 2.0 Onboard Profiles (feature 0x8100), the flash profile memory G-series gaming mice use to decide whether they run host software settings or their own stored profiles. Until now 0x8100 was a name-only row in the feature registry, so OpenLogi could neither report which mode a mouse was in nor let the user choose one.

This is a read and mode-control slice: read the memory description, mode, active profile and profile directory; switch between host and onboard mode; and select the active onboard profile. It deliberately does not write flash, so profile editing (functions 6 to 8) is out of scope.

The mode lives in device RAM, so a mouse left in host mode is back in onboard mode after a power cycle. The agent therefore re-applies a configured mode on every reconnect. A device with no configured mode is left in whatever mode it powered on in, and OpenLogi never switches it on the user's behalf.

Scope, and what still needs work

This PR is the wiring: the protocol wrapper, the I/O verbs, the config key, a panel to toggle the mode and pick a profile, and enough receiver support to reach these mice at all. It is not a finished onboard-profiles feature:

  • Profile contents are untouched. Key bindings, DPI stages and report rate stored inside a profile are neither read nor written. Selecting a profile activates whatever the device already has in that slot.
  • The UI is functional, not designed. The Profiles tab is a source toggle and a row of pills. It needs real design work before it is something to ship proudly.
  • Flash writes are out of scope, so OpenLogi cannot create, edit or reset a profile.

Worth reviewing as the foundation, not as the finished surface.

Changes

  • hidpp: new feature/onboard_profiles with getDescription, get/set onboard mode, get/set current profile, memoryRead, and directory parsing over sector 0. Offsets are reverse-engineered against Solaar and libratbag and marked as such; unknown mode and enabled bytes surface as UnsupportedResponse rather than silent fallbacks. The directory read is bounded by profile_count + profile_count_oob, so a device that lists its read-only profiles is not truncated. No flash-write session.
  • hid: IPC-facing ProfilesMode / ProfileEntry / OnboardProfilesInfo and the read/apply verbs; apply_profiles_config skips writes the device already matches. Recognises Lightspeed receivers (0xc53f, 0xc547) so a G502 X LIGHTSPEED is reachable at all. Adds the exchange() lock described below.
  • core: per-device [devices."…".onboard_profiles] (mode, profile), config-file only and now documented in docs/CONFIGURATION.md; Capabilities::onboard_profiles from a 0x8100 feature probe.
  • ipc: set_onboard_profiles / read_onboard_profiles agent methods and the reconnect re-apply. PROTOCOL_VERSION 10 → 11, wire-format goldens regenerated.
  • gui: a Profiles tab gated on Capabilities::onboard_profiles, with a settings source (OpenLogi settings / onboard memory) and the active-profile selector. The selector offers user slots only: a device's read-only profiles are factory templates a slot is reset from, and the firmware rejects setCurrentProfile on one, so offering them would be an action that cannot succeed. diag profiles still prints them, since showing raw device state is its job.
  • cli: openlogi diag profiles prints the state and runs a mode/profile round-trip (--read-only, --leave-onboard).

Things worth a careful look

PROTOCOL_VERSION 10 → 11. ProfilesMode and ProfileEntry cross the agent↔GUI IPC, so their variant and field order are wire format.

The global lock is a stopgap, not the fix. send_v20 matches a reply to its request by comparing HID++ headers, and the channel is built with rotate_software_id: false, so two concurrent requests to one device carry byte-identical headers and can take each other's replies. Bench-observed on a G502 X: a DPI write racing a mode write fails InvalidArgument 3/3 with the DPI payload landing on 0x8100's feature index, and get_dpi racing a mode write returns Ok(0), silently wrong with no error. write::exchange() serializes verbs to stop it, but the root fix is HidppChannel::set_rotating_sw_id(true). The lock is global rather than per-device and is held across open_route_channel(), so it serializes enumeration for unrelated devices too, which is heavier than the problem warrants. Rotating the software id is left out of this PR deliberately: it changes the vendored channel for every device and every feature, so it wants the bench cases re-run on hardware rather than riding along here. Note also that gesture.rs::run_capture_session opens a channel outside the lock.

Lightspeed receivers route as DeviceRoute::Unifying. They speak the same HID++ 1.0 register protocol, so they are enumerated, routed and paired through the Unifying path, and only receiver_display_name tells them apart. A dedicated route variant would fork every match arm for what is currently a cosmetic difference, so it seems worth revisiting only if their pairing register semantics turn out to diverge.

Three new UI strings are English placeholders in all 19 non-English locales, for Crowdin to fill.

The device renders as a silhouette. The pinned asset catalog has no G502 X or X-generation entry (#461), so the panel shows generic art and hotspots.

Testing

Hardware: G502 X LIGHTSPEED over a Lightspeed receiver, on Windows.

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo run -p openlogi -- diag profiles      # read + round-trip
cargo run -p openlogi -- diag dpi           # in both modes
cargo run -p openlogi-gui                   # Profiles tab, toggle + re-read

Verified on hardware:

  • diag profiles reads the description (5 user + 2 read-only profiles, 11 buttons, 16 × 255 B sectors), the mode, the active profile and the directory. The strict 0/1 enabled parse holds on real flash, and the unit fixture carries this captured payload.
  • Mode and active-profile round-trips succeed, including across an agent restart honouring a GUI-set mode = onboard, profile = 2.
  • setCurrentProfile is onboard-mode only. Host mode answers InvalidArgument and reports the active profile as 0x0000, since it parks the flash profile.
  • The mode is volatile: a device left in host mode came back onboard, with sector 0x0002 active, after a power cycle.
  • DPI writes are accepted in both modes. 1600 → 1650 → 1600 round-trips while onboard, while in host mode, and immediately after a mode write.
  • The concurrency pairs above, before and after the lock.

Not verified:

  • Any device other than a G502 X LIGHTSPEED. Everything above generalises from one mouse.
  • The read-only-profile paths. The G502 X reports 2 of them alongside its 5 user slots, yet the sector-0 directory terminates after the 5 user entries and setCurrentProfile rejects 0x0101 to 0x0103, so nothing here exercises them. A device that does list them would be the first real test.
  • macOS and Linux GUI builds and i18n parity are CI's job; not built on macOS locally.

Notes

Depends on #388 (Lightspeed receiver recognition). Its commit is carried here so the branch builds and the hardware test runs; the patch-ids match, so it drops out on rebase once #388 merges.

Screenshots

Profiles tab: settings source Profiles tab: active profile

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds full read-and-mode-control support for HID++ 2.0 0x8100 OnboardProfiles on G-series gaming mice, along with Lightspeed receiver recognition (0xc53f, 0xc547) that makes devices like the G502 X LIGHTSPEED reachable at all. The implementation spans the full stack: a new openlogi-hidpp feature wrapper, HID-layer verbs, agent IPC (PROTOCOL_VERSION → 11), a reconnect reapply path that sequences before DPI writes (to prevent flash-reload clobber), and a GUI Profiles tab with source toggle and profile pills.

  • Protocol layer (openlogi-hidpp): strict enabled-byte validation, total-profile directory reads (user + ROM), and a global EXCHANGE mutex to serialize concurrent HID++ verbs — acknowledged as a stopgap pending rotating software IDs.
  • Reconnect sequencing (orchestrator.rs): apply_onboard_profiles_in_background runs first with a RunOnDrop continuation; DPI/SmartShift writes land only after the mode settles, preventing a flash-profile reload from discarding them.
  • GUI (profiles_panel.rs): ROM profiles are correctly excluded from the user-facing selector; optimistic write + confirming re-read pattern mirrors the SmartShift panel; keep_profile_for carries the current sector through a mode switch so switching back to onboard restores the same profile.

Confidence Score: 4/5

Safe to merge after fixing the ROM-sector omission in the diag command; all other paths are well-guarded and hardware-tested.

The diag command's profile_round_trip selects a target profile without filtering ROM sectors, while the GUI and config-apply paths both do. Now that get_onboard_profiles passes the total entry count (user + OOB), a device that lists enabled ROM entries in sector 0 would have set_active_profile called on a ROM sector, which the firmware rejects. The rest of the PR is solid.

Files Needing Attention: crates/openlogi-cli/src/cmd/diag/profiles.rs — profile_round_trip target selection needs !e.is_rom() to match the GUI guard.

Important Files Changed

Filename Overview
crates/openlogi-cli/src/cmd/diag/profiles.rs New diag command reads description/mode/profile/directory and does mode and profile round-trips. profile_round_trip is missing !e.is_rom() filter, so a device with enabled ROM entries in its directory would cause a firmware-rejected InvalidArgument on set_active_profile.
crates/openlogi-hid/src/write/onboard_profiles.rs HID-layer verbs for 0x8100: get_onboard_profiles correctly passes total_profiles (user+OOB) to read_profile_directory; apply_profiles_config_on_channel skips writes when already matching; read-back warnings are non-fatal.
crates/openlogi-hidpp/src/feature/onboard_profiles/mod.rs New 0x8100 feature impl: get_description, mode get/set, profile get/set, memory_read, directory parse. Loop termination and bounds are correct; doc comment on read_profile_directory is stale after the total-profiles fix.
crates/openlogi-hidpp/src/feature/onboard_profiles/types.rs Domain types for 0x8100: OnboardMode, ProfilesDescription, ProfileDirectoryEntry, parse_directory. Wire layout matches libratbag; strict enabled-byte validation is correct; tests cover all edge cases.
crates/openlogi-agent-core/src/orchestrator.rs Reconnect reapply now calls apply_onboard_profiles_in_background before the DPI/SmartShift continuation; configured_onboard_profiles correctly gates on capabilities.onboard_profiles; sequencing prevents flash-profile reload from clobbering a DPI write.
crates/openlogi-agent-core/src/hardware.rs apply_onboard_profiles_in_background uses RunOnDrop to guarantee the continuation fires on panic/early-return; integrates with shared-channel fast path correctly.
crates/openlogi-hid/src/write.rs Introduces global EXCHANGE mutex to serialize HID++ verbs; acknowledged as a stopgap (per-device would be better, and gesture.rs bypasses the lock); all *_on shared-channel helpers now take the lock before delegating.
crates/openlogi-gui/src/components/profiles_panel.rs New profiles tab: source toggle and profile pills. ROM entries are correctly excluded from the selector; optimistic write + confirming re-read pattern matches smartshift_panel; keep_profile_for handles host-mode 0x0000 correctly.
crates/openlogi-gui/src/state.rs LazyDeviceData wiring for profiles follows the established smartshift pattern; commit_onboard_profiles correctly gates the profile write on mode==Onboard in apply_profiles_config_on_channel.
crates/openlogi-agent-core/tests/wire_format.rs PROTOCOL_VERSION bumped to 11; golden bytes for new request variants (SetOnboardProfiles, ReadOnboardProfiles), ProfilesMode, OnboardProfilesInfo, and HidppOperation indices are all pinned.

Sequence Diagram

sequenceDiagram
    participant GUI as GUI (profiles_panel)
    participant State as AppState
    participant IPC as Agent IPC
    participant HW as hardware.rs
    participant HID as write/onboard_profiles.rs
    participant Dev as Device (0x8100)

    GUI->>State: commit_onboard_profiles(mode, profile)
    State->>State: optimistic update (Ready to new mode)
    State->>IPC: SetOnboardProfiles(route, mode, profile)
    State->>State: persist to config.toml
    State->>State: profiles_pending_confirm.insert(key)

    IPC->>HW: apply_onboard_profiles(capture, route, mode, profile)
    HW->>HID: apply_profiles_config_on_channel(channel, index, mode, profile)
    HID->>Dev: get_onboard_mode() [read current]
    Dev-->>HID: current mode
    HID->>Dev: set_onboard_mode(mode) [if changed]
    HID->>Dev: get_onboard_mode() [read-back]
    HID->>Dev: set_current_profile(sector) [if onboard and profile changed]
    HID->>Dev: get_current_profile() [read-back]
    HID-->>IPC: Ok(written)

    Note over GUI,State: On next render
    GUI->>State: ensure_profiles_confirm()
    State->>IPC: ReadOnboardProfiles(route)
    IPC->>HW: read_onboard_profiles(route)
    HW->>HID: get_onboard_profiles(route)
    HID->>Dev: get_description() + get_onboard_mode() + get_current_profile() + read_profile_directory(total)
    Dev-->>HID: OnboardProfilesInfo
    HID-->>State: store_profiles_info(key, route, Ok(info))
    State->>GUI: notify (render with real state)
Loading

Fix All in Codex Fix All in Claude Code

Reviews (10): Last reviewed commit: "feat(hid,hidpp): recognize Lightspeed re..." | Re-trigger Greptile

Comment thread crates/openlogi-hid/src/write/onboard_profiles.rs
Comment thread crates/openlogi-hid/src/write/onboard_profiles.rs
@davidbudnick

Copy link
Copy Markdown
Collaborator

@Stanley5249 Looks as you have issues, with your pipeline running for the following change, can you fix issues and re-push?

@davidbudnick
davidbudnick force-pushed the feat/onboard-profiles branch from bd4daa2 to b57c81b Compare August 2, 2026 00:53
@Stanley5249
Stanley5249 force-pushed the feat/onboard-profiles branch from b57c81b to be81dd5 Compare August 3, 2026 05:03
@Stanley5249
Stanley5249 force-pushed the feat/onboard-profiles branch from be81dd5 to 10f99f6 Compare August 3, 2026 05:17
@Stanley5249

Copy link
Copy Markdown
Contributor Author

@davidbudnick CI is green now, and I've updated the PR body. A few things could use a closer look: the protocol version bump, the translations, and the temporary global lock. Details on each are at the top. Thanks.

@davidbudnick

Copy link
Copy Markdown
Collaborator

@davidbudnick CI is green now, and I've updated the PR body. A few things could use a closer look: the protocol version bump, the translations, and the temporary global lock. Details on each are at the top. Thanks.

Perfect thanks for updating it, looks as you have a few merge conflicts which need to be resolved before a merge can take place.

Stanley5249 and others added 18 commits August 3, 2026 23:13
Memory description, onboard/host mode, active profile, flash sector
reads and profile-directory parsing. Protocol cross-checked against
libratbag hidpp20.c and Solaar hidpp20.py (official x8100 spec is not
public). The flash write session (functions 6-8) is deliberately not
implemented.
ProfilesMode / ProfileEntry / OnboardProfilesInfo boundary types (serde,
IPC wire format from day one), get_onboard_profiles, set_profiles_mode
and set_active_profile with read-back, and apply_profiles_config — the
agent-facing verb that skips writes the device already matches. Appends
ReadOnboardProfiles / WriteOnboardProfiles to HidppOperation (wire-safe
append; goldens updated with the PROTOCOL_VERSION bump).
OnboardProfiles { mode: host|onboard, profile } persisted per device so
the agent can re-apply it on reconnect (the mode reverts to onboard on a
power cycle). Config-file only; the default policy for an unconfigured
device is host mode so OpenLogi's settings apply.
Appends set_onboard_profiles / read_onboard_profiles to the Agent trait
and Capabilities::onboard_profiles (0x8100) — PROTOCOL_VERSION 10 -> 11,
wire goldens regenerated. The orchestrator applies the configured mode
(default: host, so software settings work on gaming mice) first in the
reconnect reapply path; the mode is RAM-volatile and reverts to onboard
on power cycle. Devices without 0x8100 skip at debug level.
…ng path

Mirrors upstream PR AprilNEA#388 (kiwimaker) rebased onto current master, with the
G502 X LIGHTSPEED receiver (046d:c547) added and hardware-verified on
Windows 11: enumeration, feature dump, and a diag dpi round-trip. Also
chains LIGHTSPEED_PIDS into the Linux receiver-child filter and refreshes
the stale Bolt-only note in the CLI list help.

Co-authored-by: kiwimaker <49567144+kiwimaker@users.noreply.github.com>
Profiles tab gated on Capabilities::onboard_profiles: a settings-source
control (OpenLogi settings vs onboard memory) and an active-profile
selector for the enabled directory entries. Commits persist to
config.toml and apply over IPC with an optimistic cache + confirming
re-read, on the SmartShift panel's lazy-read pattern. New locale keys
seeded with English values across all locales (Crowdin translates).
Reads the 0x8100 description, mode, active profile, and profile
directory, then round-trips the mode and the active profile with
read-back verification and restore. --read-only skips the writes;
--leave-onboard keeps the device in onboard mode for verifying the
agent's reapply. The profile write runs inside the onboard-mode window
because host mode rejects setCurrentProfile (verified on a G502 X).
A G502 X still in onboard mode answered the parallel DPI reapply with
InvalidArgument — the mode switch and the other volatile writes raced
in separate threads. apply_onboard_profiles_in_background now takes the
rest of the reapply as a continuation and runs it after the mode apply
settles; devices without 0x8100 run it immediately as before. Also
tolerate read-back failures after mode/profile writes (the read races
the device's mode transition) like the DPI path, and document that
setCurrentProfile is onboard-mode-only.
The 0x0100 ROM flag was open-coded in ProfileEntry::is_rom and again in the
CLI's info printer, while hidpp already exports ROM_SECTOR_FLAG. Route both
through openlogi_hid::is_rom_sector, which takes a bare sector so callers
holding only active_profile can ask too.
diag profiles forces the device into onboard mode before the active-profile
round-trip and restores the original mode at the end. Every `?` and `bail!` in
between returned past that restore, leaving the device in onboard mode — which
is exactly when it matters, since a failed run is when the user is least likely
to notice the mouse stopped honouring host settings.

Split the round-trip and the restore into their own functions so the restore
always runs, and report the round-trip error first when both fail.
apply_onboard_profiles_in_background carries the rest of the reconnect volatile
reapply as its `after` continuation, and called it as the last statement of the
spawned thread. A panic on the way there — runtime build, block_on, logging —
dropped every remaining write with nothing to show for it. Run the continuation
from a drop guard instead, which also covers the runtime-init early return.
The pill labels enumerated the enabled-filtered list, so a disabled slot
renumbered every profile after it and the labels stopped lining up with the
slot numbers G HUB shows. Enumerate the directory, then filter.
configured_onboard_profiles was inserted between configured_wheel_mode's
rustdoc and its signature, so the HiResWheel doc described the wrong function
and the wheel helper was left undocumented.

VPID_PAIRS claimed both new Lightspeed receivers were verified against
hardware, but only the G502 X LIGHTSPEED (0xc547) was on the bench; 0xc53f
comes from upstream hidpp PR AprilNEA#388.
configured_onboard_profiles fell back to host mode for any 0x8100 device with
no onboard_profiles config, so the agent switched a gaming mouse out of onboard
mode on every reconnect without being asked. The mode is user-visible state the
mouse changes on its own — a G502 X boots into onboard mode and reports its
active profile only by blinking an LED — so taking it over uninvited silently
overrode the profile the user picked on the device itself.

Return None when nothing is configured and leave the device in whatever mode it
powered on in. A configured mode is still re-asserted on every reconnect, since
the device does not remember host mode.
The 0x8100 feature spans hidpp, hid, agent-core, gui and cli, and its
shape is
dictated by firmware behaviour that is invisible in the code: the device
boots
into onboard mode and forgets host mode, onboard mode rejects DPI writes
while
host mode rejects setCurrentProfile, and the mouse reports its active
profile
only by blinking an LED.

Map the feature across the five crates, then record each constraint
together
with the code decision it forces — why the volatile reapply is a
continuation
rather than parallel writes, why the continuation runs from a drop
guard, and
why a device nobody configured is left alone. Call out the known gaps as
current
state rather than bugs, and keep the hardware questions listed as
unverified so
they are answered on a device instead of reasoned about.

Sources the Logitech setup guide for the device facts and marks bench
observations as observed, since the 0x8100 spec is not public.
configured_onboard_profiles is private, so its rustdoc and
.claude/rules/onboard-profiles.md address the same readers — the consent
rationale was written out twice. Keep the contract in the rustdoc, the argument
in the rule. Same for the firmware facts already documented at memory_read,
DIRECTORY_END and is_rom_sector, and for RunOnDrop restating its own function
doc.
…ching

HID++ correlates a reply to its request only by (device index, feature
index, function, software id), and every verb opens by resolving its
feature with the same 0x0000 getFeature header at the channel's fixed
software id. Two verbs in flight on one device therefore match each
other's replies.

Bench-observed on a G502 X LIGHTSPEED: set_profiles_mode || set_dpi
failed InvalidArgument 3/3 (the DPI payload reaching 0x8100's index),
set_dpi || set_scroll_wheel_mode reported 0x2121 unsupported though the
feature table lists it, and get_dpi || set_profiles_mode returned Ok(0)
-- silently wrong, no error. Sequentially all of them succeed.

An exchange() lock now spans each verb, taken in with_route and in every
*_on shared-channel fast path. All four cases pass after the change.
The description fixture was hand-invented and disagreed with the real
device on two fields: 3 ROM profiles instead of 2, and 256-byte sectors
instead of 255. The sector size is the one that matters -- it feeds the
`sector_size - 16` read bound, so a rounded-up 256 would hide an
off-by-one there.
"Onboard mode rejects host writes with InvalidArgument" was wrong. On a
G502 X LIGHTSPEED, DPI round-trips 1600->1650->1600 in onboard mode, in
host mode, and immediately after a mode write; the rejection that started
this belonged to the concurrent-request cross-talk fixed one commit back.

The reapply still has to run after the onboard-profiles apply, for the
reason that actually holds: activating a profile reloads that profile's
DPI out of flash, so a write that raced ahead of it would be discarded.

Bench facts added: host mode rejects setCurrentProfile and reports active
profile 0x0000 (the flash profile is parked); the mode is volatile,
confirmed by a host-mode device returning onboard after a power cycle;
host mode leaves G7/G8 dead and the LED dark, because OpenLogi writes the
mode byte and none of what the parked profile supplied. The device
exposes no 0x1b04 and no 0x807x lighting, which bounds how far that can
be closed -- and it is a fresh argument for keeping host mode opt-in,
replacing the "configured DPI silently no-ops" cost that does not exist.
parse_directory stops at max_entries even when the terminator has not
been reached, so passing profile_count alone truncates the directory on
any device that lists its ROM profiles after the user ones.

Bounding by profile_count + profile_count_oob costs nothing -- the read
loop already stops early at the terminator -- and the terminator stays
the real end of the directory.

Reported by Greptile on AprilNEA#459. Its worked example does not reproduce: a
G502 X terminates the directory right after the 5 user entries, and
rejects set_current_profile for 0x0101/0x0102/0x0103 with
InvalidArgument, so ROM profiles there are counted in the description but
neither listed nor selectable. The bound is still wrong in principle, and
a device that does list them would lose entries.
Public `set_dpi_on` linked pub(crate) `exchange`; the onboard-profiles docs
gave explicit link targets their labels already resolve to.
The firmware rejects setCurrentProfile on a ROM sector, so a ROM pill was an
action that could not succeed. Filter ROM entries out of the pill list and
never let keep_profile_for pick one; the "ROM profile %{n}" string goes with
them. `diag profiles` still prints ROM entries — showing raw device state is
its job.
The module doc described a host-mode device as having "a dark LED", which
conflates a DPI indicator with RGB the reference device does not have; state
only the DPI stage list. ProfileSource::Host called itself "the default",
which contradicts the agent leaving unconfigured devices alone.
It is config-file only, so the TOML reference was the only place a user could
learn it exists.
Protocol facts belong in the onboard_profiles rustdoc, the config key in
docs/CONFIGURATION.md; the rule keeps only what an agent could otherwise
violate. Drops the device-behaviour diagrams and the bench narrative, and
gives the AGENTS.md row path globs like every other row.
@Stanley5249
Stanley5249 force-pushed the feat/onboard-profiles branch from 6817f7b to 0b6da4b Compare August 3, 2026 15:15
Lightspeed receivers route as DeviceRoute::Unifying because they speak the
same HID++ 1.0 register protocol, so route_label named them Unifying. Carry
the receiver product ID on DeviceRecord and pick the label from it.
@Stanley5249
Stanley5249 force-pushed the feat/onboard-profiles branch from 0b6da4b to e42a2c1 Compare August 3, 2026 15:20
@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Start a greploop in Codex and it will work through the open comments and keep going until this PR reviews clean.

The confirm set was only drained for the current device, so a rerouted or
removed one left a stale key behind. Mirrors the smartshift cleanup.
@Stanley5249

Copy link
Copy Markdown
Contributor Author

@davidbudnick Done. Maybe close #388 as well, its commit is carried here with kiwimaker credited.

Co-authored-by: Abnersouza7 <65561500+Abnersouza7@users.noreply.github.com>
Comment thread crates/openlogi-cli/src/cmd/diag/profiles.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants