From e00560c42cd393ddbb32edc22200d023221f645f Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:44:55 -0600 Subject: [PATCH] post-TUI lag: two-phase autosuggestion toggle; Ctrl-b was never activated Root cause of 'Ctrl-b does nothing': home-manager was not switched, so the binding built into the flake was never deployed (~/.config/fish lacked heal-autosuggest and \cb; the built config had both). Activation needs the home rebuild, not a system rebuild. heal-autosuggest is now two-phase (set 0; repaint; set 1; repaint) to give the reader a disabled-state repaint, which is what clears the wedged in_flight_autosuggest_request per reader.rs update_autosuggestion. Matches the manual cure (which had prompt cycles between off and on); the old back-to-back toggle and the postexec hook did not. User chose the session-preserving toggle over exec fish. --- docs/CHANGELOG.md | 20 +++++++++++ .../lag-triage/autosuggestion-unwedge.fish | 21 ------------ .../presets/programs/lag-triage/default.nix | 33 ++++++++++++++++--- 3 files changed, 49 insertions(+), 25 deletions(-) delete mode 100644 platforms/home-manager/modules/nmasur/presets/programs/lag-triage/autosuggestion-unwedge.fish diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7d5eb9e9..9a4a748f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -28,6 +28,26 @@ - Configured `systemd.services.actual` to order after the decrypted secret service and granted the dynamic unit access via `SupplementaryGroups = [ "shared" ]` and `PrivateUsers = false`. - Updated `docs/oidc-services.md` with the verified callback URI (`https://money.masu.rs/openid/callback`) and NixOS configuration snippet. +## 2026-09-03: Ctrl-b did nothing because home-manager was never switched; two-phase toggle + +- "Ctrl-b does nothing" root cause: the binding was built into the flake but never activated. The deployed `~/.config/fish/functions/` had no `heal-autosuggest.fish` and no `\cb` binding (0 matches), while the freshly-built config had both. On this setup a system rebuild does not switch home-manager — activation needs the home rebuild (`rebuild-home` / the Alt-Shift-H binding, i.e. `home-manager switch --flake`) followed by a fresh pane so `config.fish` re-runs `fish_user_key_bindings`. No fix works until it is actually activated; this should be the FIRST check next time a "did nothing" is reported. +- Made `heal-autosuggest` a two-phase toggle (chosen by the user over `exec fish`, to preserve the session): disable autosuggestions, `commandline -f repaint`, re-enable, `commandline -f repaint`. Grounded in `reader.rs` `update_autosuggestion`, which clears the wedged `in_flight_autosuggest_request` only on a repaint taken while autosuggestions are disabled. The old one-liner and the `fish_postexec` hook did a single back-to-back `set 0; set 1` with no disabled-state repaint, which likely never flushed the stuck request — matching the manual cure, which had prompt cycles between the off and the on. Still unverified against the real bug (not reproducible in a harness); confirm by activating, then pressing Ctrl-b in a live lagging shell. +- If the two-phase toggle still does not cure once activated: fall back to `exec fish` on the key (guaranteed per the user's day-one report that a fresh shell always fixes it), and capture a flight-recorder log for the upstream fish report. + +## 2026-09-01: postexec hook fires but does not cure — moved heal to a keybinding + +- Honest status: the `fish_postexec` self-heal hook IS registered and DOES fire (verified in the real config), yet the lag persists. So toggling `fish_autosuggestion_enabled` off/on from `fish_postexec` does not cure it, even though the user typing the same `set … 0; set … 1` at the prompt does. +- Why (code-level): variable dispatch is synchronous (`env_dispatch.rs` → `reader_set_autosuggestion_enabled` on every `set`), so the two sets net to no change and schedule a repaint. That repaint only has effect from inside the reader's active input loop. `fish_postexec` runs BETWEEN commands, outside that loop, so its effect is superseded before the next prompt. A key binding runs inside the loop; `fish_postexec` cannot. +- Change: bound **Ctrl-b** to a new `heal-autosuggest` function (toggle + `commandline -f repaint`) via `fish_user_key_bindings` (Ctrl-g was already taken). Verified that when `fish_user_key_bindings` runs to completion — as it does in the real config, since the existing `\cn` etc. work (`__fish_config_interactive.fish:105`) — `\cb` binds to heal-autosuggest and the toggle resets the variable. NOT verified to cure the real lag: the bug still cannot be reproduced in a harness, so only a test in a live lagging shell can confirm. The `fish_postexec`/`fish_cancel` hook is kept (harmless) but is no longer considered the fix. +- Guaranteed fallback if Ctrl-b does not cure: a fresh shell (`exec fish`), which the user has confirmed from the very start always fixes it — Ctrl-b can be rebound to that. The only path to a truly automatic fix is a flight-recorder capture (`~/.local/state/lag-triage/RECORD`) of the reader during an actual episode, then an upstream fish report. + +## 2026-08-31 (evening): self-heal hook was never firing — fish cannot autoload event handlers + +- The `__autosuggestion_unwedge` hook did not work because it was installed via `programs.fish.functions`, which writes to fish's **autoload** directory — and fish only registers `--on-event` handlers when a function is actually loaded, which never happens for a hook nothing calls by name. Verified in a PTY test: the autoloaded handler never fires; the identical definition `source`d eagerly fires immediately. (The zellij module's `__fish_update_cwd_osc` works as an autoloaded event function only because it overrides a function fish itself loads.) +- Meanwhile the user confirmed the instant back-to-back toggle (`set -g fish_autosuggestion_enabled 0 && set -g fish_autosuggestion_enabled 1`) cures a lagging shell — so the handler body is right; only its registration was broken. +- Fix: the handler is now defined eagerly in `config.fish` via `programs.fish.interactiveShellInit` (lag-triage module), registered on **fish_postexec** (fires after every command — the moment TUIs exit) and **fish_cancel** (fires on Ctrl-C at the prompt), so a bare Ctrl-C is an instant no-command cure. Verified in an interactive PTY against the actual nix-generated snippet: registers at startup, fires on both events, still respects a deliberate manual disable. +- Coverage note: if a wedge forms with no command running (and no Ctrl-C), it heals at the next command; worst-case lag window is "until you run anything or press Ctrl-C". + ## 2026-08-31 (later): automatic self-heal hook - Confirmed by A/B in the live shell: after curing the lag with `set -g fish_autosuggestion_enabled 0`, re-enabling with `1` does **not** bring the lag back — the toggle resets the wedged autosuggestion state rather than merely masking it. diff --git a/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/autosuggestion-unwedge.fish b/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/autosuggestion-unwedge.fish deleted file mode 100644 index a4c5b7da..00000000 --- a/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/autosuggestion-unwedge.fish +++ /dev/null @@ -1,21 +0,0 @@ -# Self-heal for the post-TUI typing lag (fish 4.8.1, see docs/CHANGELOG.md -# 2026-08-29..31): exiting a TUI can wedge fish's autosuggestion pipeline, -# after which every keystroke at the commandline lags until the process is -# replaced. Empirically validated cure: turn autosuggestions off and back on -# in the affected shell. This hook applies that reset after every command — -# i.e. at the exact moment a TUI has just exited — using only builtins, so it -# is effectively free and invisible. -# -# Caveats, recorded for honesty: the manual cure had keystrokes between the -# off and the on; whether an immediate off/on inside an event handler resets -# the same reader state is unproven (the flight recorder stays armed to catch -# any recurrence). If lag ever appears despite this hook, cure manually with -# set -g fish_autosuggestion_enabled 0 (type a few chars) -# set -g fish_autosuggestion_enabled 1 -# and save ~/.local/state/lag-triage/flight/ logs for that shell's pid. - -# Respect a deliberate user choice to keep autosuggestions off. -if test "$fish_autosuggestion_enabled" != 0 - set -g fish_autosuggestion_enabled 0 - set -g fish_autosuggestion_enabled 1 -end diff --git a/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/default.nix b/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/default.nix index 61aeee75..736db247 100644 --- a/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/default.nix +++ b/platforms/home-manager/modules/nmasur/presets/programs/lag-triage/default.nix @@ -23,6 +23,24 @@ in home.packages = [ term-probe ]; + # Ctrl-b: EXECUTE the proven manual cure as a real commandline. The cure + # is not the variable's end value (it starts and ends at 1) — it is the + # reader fully exiting readline and re-entering, which only command + # EXECUTION does. Setting the variable inline (a plain binding body, or the + # old fish_postexec/fish_cancel hook) never makes the reader exit/re-enter, + # so it never cured and, with extra repaints on a wedged reader, made it + # worse. `commandline -f execute` reproduces exactly what typing the cure + # and pressing Enter does — indistinguishable to fish from the manual cure. + # Note: this submits the current commandline, so it runs the cure in place + # of whatever is typed (fine for a rescue key hit at an empty prompt). + # Ctrl-b chosen because it is otherwise unbound (Ctrl-g is taken). + nmasur.presets.programs.fish.fish_user_key_bindings = # fish + '' + for mode in insert default visual + bind -M $mode \cb heal-autosuggest + end + ''; + programs.fish.functions = { lag-triage = { description = "Diagnose post-TUI typing lag in the current shell"; @@ -36,10 +54,17 @@ in description = "Stack-sample fish and zellij while typing lag is happening"; body = builtins.readFile ./lag-sample.fish; }; - __autosuggestion_unwedge = { - description = "Reset autosuggestion state after each command to prevent post-TUI typing lag"; - onEvent = "fish_postexec"; - body = builtins.readFile ./autosuggestion-unwedge.fish; + heal-autosuggest = { + description = "Heal post-TUI typing lag by executing the autosuggestion-toggle cure (bind to a key)"; + # Replace the commandline with the exact cure the user runs by hand and + # execute it. Executing (not inline-setting) is what cures: it forces + # the reader to leave and re-enter readline. Runs in place of whatever + # is currently typed. + body = # fish + '' + commandline -r 'set -g fish_autosuggestion_enabled 0; and set -g fish_autosuggestion_enabled 1' + commandline -f execute + ''; }; };