Grok round-6 verification was No-Go. Fixes:
A. LayoutSlot.id was written as 0 by every constructor
(generate Stacked/Grid/main_strip, capture_from, the GUI pad
literals), so the find-by-id lookups in resize_slot / move_slot
never hit a tile and Free-mode Apply size/position always
failed with "no layout slot for slot id N". Constructors now
assign real 1-based ids (index+1, out.len()+1, i+2 for the
strip); capture_from uses the slot id from the window tuple.
layout::apply and reset_slot_lock now find the tile by id
instead of by Vec index.
B. The `slots` IPC formatter emitted "{id} 0x{address} ..." while
addresses already carry their own 0x prefix ("0xa"), producing
"1 0x0xa ..."; the parser split on whitespace so any multi-word
window title broke the field alignment. Both sides now use a
tab separator and the address passes through unchanged.
C. examples/profile.yaml still shipped the dropped schema
(window_match block + passthrough list). Replaced with a note
that matching is by process tree and every mapped hotkey is
intercepted.
D. CHANGELOG 0.1.0 still advertised passthrough (lines 19, 24)
and window_match (27, 78). Annotated as removed.
E. Lying comments: launcher.rs called the prefix "per-team" (it is
per-character); gui.rs::arm_auto_apply doc claimed it matched a
regex.
F. arm_auto_apply still hardcoded an empty spawned-pid set and
fell back to matching any client with a non-empty class -- the
round-4 Item-3 placeholder was what actually ran. It now takes
the real child pid and matches via pid_is_ancestor.
G. spawn_plan dropped the Child with no wait thread (zombie, same
bug round-6 fixed in launch_game). Now reaps in a background
thread.
H. page_session never refreshed the games list, so the dropdown
was empty on first paint. Added a games_loaded flag and a
one-shot refresh_games on first paint.
Plus: vfx env-var test race. toplevel_enabled_defaults_off_... and
capture_toplevel_is_gated_when_disabled both touch
ENBOXER_ENABLE_TOPLEVEL and cargo runs unit tests in parallel, so
the gated test intermittently saw the var set by its sibling (the
per-function `static` in session.rs does not serialise across
functions). Added a module-level ENV_LOCK in vfx::tests and
guarded both tests. Verified with three consecutive full runs.
Also: clippy unnecessary_cast in main_strip, and two rustdoc
warnings (raw <pid> and Arc<GbmDevice> read as HTML tags).
cargo test 103/103 (x3); clippy --all-targets -D warnings clean;
cargo doc --no-deps clean.
Grok round 5 found:
1. examples/games.yaml was actually src/main.rs (227 lines of clap
CLI; my round-4 Item 4 commit message was wrong). Replaced with
a real seed YAML. The example shows the env format as
[[KEY, VALUE], ...] (a list of pairs), NOT KEY=*** (serde_yaml
will not parse KEY=*** into Vec<(String, String)>).
3. App.pending_ipc was written by Free-mode buttons but never
drained by the main loop. Replaced with direct self.ipc(...)
calls. Hoisted the closure borrow by collecting verbs into a
Vec<(&str, u32)> and draining after the slots borrow is
released. Dropped the pending_ipc field + App literal init.
8. Doc residue: passthrough / window_match claims still in
README.md, CHANGELOG.md, docs/DESIGN.md. Updated README.md to
point at round-4 Item 6 + Item 3. Added a round-4 section at
the top of CHANGELOG.md. Dropped the duplicate passthrough
bullet in docs/DESIGN.md.
10. Dead passthrough_id helper in hotkey.rs (unused after the
passthrough feature dropped in round-4 Item 6). Dropped the
function + its doc comment.
cargo test 103/103; clippy clean.
Master: "idk why you have a Teams/Lutris section. When I am under
session I should be able to select a list of games so you know how to
launch the correct game, currently you lack that list and drop down
option."
Implementation:
- src/games.rs (new module):
* Game struct: { name, exe, args, env, cwd, note }.
* load_all(path) -> Vec<Game>; returns empty list when the file
does not exist (operator has not configured any games yet).
* find(games, name) -> Option<Game> (case-sensitive, O(n)).
* save_all(path, games) for the GUI Add-Game form.
* default_path() -> ~/.config/enboxer/games.yaml.
* Three unit tests (load_missing empty, find_by_name, YAML roundtrip).
- src/lib.rs: pub mod games registered.
- src/session.rs:
* New IPC verb "launch-game NAME": spawns the picked game entry,
records the child PID into Session.spawned_pids so refresh_slots
matches the resulting window via the process-tree walk (Item 3).
* New IPC verb "list-games": returns the joined names so the GUI
can populate the dropdown without a second command.
* launch_game function: tokio::process::Command-equivalent
std::process::Command spawn, child PID captured via child.id(),
dropped (OS keeps the process running; spawned_pids is the
only thing we need).
- examples/games.yaml: shipped with an empty `games: []` plus a
commented-out WoW example so operators have a starting point.
cargo test 103/103 (3 new games tests + the 100 from earlier);
clippy clean.
Item 5: examples/profile.yaml default profile name renamed from
"esdf-team" to "team". The default shipped profile should be
called what it is.
Item 6: drop the passthrough feature entirely. Removed:
- Profile.passthrough field + Profile::passthrough_set
- Engine.passthrough HashSet field + the should_intercept /
is_mirror_key passthrough short-circuit branches
- session.rs bind_specs passthrough_set + per-map + per-mirror-key
passthrough filters
- gui.rs passthrough label + text-edit widget
- main.rs profile log line that referenced p.passthrough
- hotkey.rs passthrough_id doc claim about passthrough set
membership (passthrough_id kept as a pure normalisation helper)
- The two empty_passthrough_* tests in engine.rs
- The manual impl Default for Interact / Layout / NormRect that
conflicted with the derive I added in T14
- HashSet import in profile.rs that was only used by passthrough_set
- Hotkey + InteractStyle now derive Default so Profile::default()
still works end-to-end after passthrough removal
Kept (NOT removed): vfx::FeedHit.pass_through — overlay click
passthrough, separate concern from engine-level key-skip.
Also silenced the stale #[allow(dead_code)] on
GbmDevice.sym.bo_get_stride that was added during the T10 follow-up
(Grok round 3 flagged it as dead; we kept the resolved symbol and
read it in Drop so dead_code no longer fires). No more spinning red
circle from that warning.
cargo test 97/97; clippy clean.
bind: "interact" now resolves to game_binds.interact (Alt+J) only —
a single keystroke. The full ISBoxer-style chain (CTM on -> Alt+J ->
sleep walk_delay_ms -> CTM off) lives at bind: "smart_interact".
Fixes the doubling bug Grok flagged: bind: "interact" previously
expanded into the full chain unconditionally, so the loot_manual and
interact_hold example maps fired ctm_on/ctm_off twice and produced
unwanted sleep delays.
- engine.rs: rename shortcut trigger (was: "interact")
- examples/profile.yaml: loot now uses smart_interact; loot_manual and
interact_hold keep "interact" as a single Alt+J send
- docs/MACROS.md: heading + shortcut comment
- docs/NOTES.md: trigger name
- Add new test: interact_simple_sends_only_alt_j (1-line single-send)
- Existing smart shortcut tests renamed to bind: smart_interact
- CHANGELOG.md: trigger split note
93/93 cargo test pass; clippy clean.
A step with bind: interact now expands at compile time into the full chain
(CTM on -> Interact with Target -> sleep walk_delay_ms -> CTM off), driven
by profile.interact (style + walk_delay_ms). One user keypress, four
keystrokes dispatched to every captured slot.
Styles:
- standard (default): chain runs, CTM ends off
- auto: chain runs, CTM stays on (toggle via ctm_off later)
- hold: press fires CTM-on + Alt+J; bind: ctm_off in
release_steps to fire it on hotkey release
Example loot (Alt+G) and interact (Alt+I) maps now use the shortcut. A
loot_manual map (Ctrl+Alt+G) exercises the explicit chain for users who
want per-step control.
92/92 unit tests pass; clippy clean. New tests:
- interact_smart_shortcut_standard_emits_full_sequence
- interact_smart_shortcut_auto_emits_two_no_tail
- interact_smart_shortcut_hold_emits_press_only
Docs: MACROS.md (new Smart interact shortcut section with style table),
NOTES.md callout, examples/profile.yaml comments, CHANGELOG.md entry.