Skip to content

Prevent intermittent TUI input stalls from chord pending state, opencode grabKeys leakage, and watcher refresh churn#930

Merged
SorraTheOrc merged 12 commits intomainfrom
copilot/fix-tui-unresponsive-keyboard-input
Mar 13, 2026
Merged

Prevent intermittent TUI input stalls from chord pending state, opencode grabKeys leakage, and watcher refresh churn#930
SorraTheOrc merged 12 commits intomainfrom
copilot/fix-tui-unresponsive-keyboard-input

Conversation

Copy link
Contributor

Copilot AI commented Mar 13, 2026

The TUI could intermittently ignore keyboard input after chord activity (notably Ctrl-W sequences), then recover later. This PR now addresses three plausible freeze paths that can suppress keyboard handling while preserving existing key mappings and UX.

  • Root cause 1 addressed: duplicate event dedupe left pending chord state without expiry

    • In src/tui/chords.ts, the duplicate-key path (lastIsSameAsNew) now re-schedules pending timeout instead of returning with a cleared timer.
    • This prevents indefinite “leader pending” suppression behavior under duplicate input delivery (screen.on('keypress') + screen.key(...) coexistence).
  • Root cause 2 addressed: opencode textarea could leave screen.grabKeys engaged after focus transitions

    • In src/tui/controller.ts, added best-effort cleanup when leaving opencode textarea contexts:
      • call textarea cancel() safely,
      • clear screen.grabKeys,
      • hide cursor.
    • Applied this cleanup on relevant focus/close paths (including Ctrl-W pane switches and opencode close/escape flows) so screen-level key handlers remain responsive.
  • Root cause 3 addressed: background directory watch events could trigger refresh churn without user input

    • The TUI’s automatic fs.watch-based database refresh in src/tui/controller.ts now gates filename-less watch events using JSONL file mtime.
    • For events where filename is missing, refresh is scheduled only when the data file mtime actually changes, reducing spurious refresh storms from unrelated directory activity.
    • Includes verbose debug logging when mtime reads fail for diagnosability.
  • Regression coverage for intermittent freeze patterns

    • test/tui-chords.test.ts: duplicate Ctrl-W delivery no longer leaves chord pending state stuck after timeout.
    • test/tui-integration.test.ts: verifies screen.grabKeys is released when switching focus from opencode input via Ctrl-W then k.
    • test/tui-integration.test.ts: verifies filename-less watch events are ignored when data mtime is unchanged, still refresh when mtime changes, and safely no-op on stat failures.
// src/tui/chords.ts (dedupe branch)
if (lastIsSameAsNew) {
  // keep pending expiry active even when deduplicating
  this.scheduleClear();
  return true;
}

[!WARNING]

Original prompt

This section details on the original issue you should resolve

<issue_title>TUI unresponsive to keyboard input</issue_title>
<issue_description>

Problem statement

Intermittent TUI freeze: keyboard input is ignored for ~30–60s and then recovers, preventing interactive use. The behaviour is sporadic with no reliable reproduction steps yet.

Users

  • Primary: Developers and operators who use the TUI interactively (searching, editing, delegating tasks) and rely on keyboard shortcuts and chords for fast workflows.
  • Secondary: Automation/agents that may interact with the TUI (indirectly) and any remote users who depend on responsive keyboard-driven controls.

Example user stories

  • As a developer using the TUI, I want keypresses to be accepted reliably so I can navigate and edit items without interruption.
  • As a keyboard-first user, I want chord shortcuts (e.g. Ctrl-W then p/h/l) to work without causing the UI to stop responding.

Success criteria

  • Repro: Provide at least one reliable reproduction path (commands, steps, environment) that demonstrates the 30–60s freeze, or
  • Root cause: Identify a plausible root cause (e.g. blocking syscall, event loop stall, chord handler deadlock) with supporting logs/traces, and
  • Fix/Mitigation: Deliver a code change or configuration that removes the freeze in the reproduced scenario, or a documented mitigation/workaround that avoids user impact, and
  • Tests / monitoring: Add at least one regression test or diagnostics/logging that would surface this failure if it reoccurs.

Constraints

  • Must preserve existing keyboard/chord behaviour and UX (no change to user-visible key mappings without explicit decision).
  • Changes should work across supported terminals (WSL/Windows Terminal, macOS iTerm2/Terminal, common Linux terminals).
  • Avoid large refactors unless evidence shows they are required; prefer targeted fixes and diagnostic instrumentation first.

Existing state

  • Current work item: WL-0MMNZWOZ60M8JY6E (status: in-progress, stage: idea, priority: critical). Description documents observed intermittent freezes and requests logs/triage steps.
  • Code: The TUI uses a ChordHandler (src/tui/chords.ts) to implement leader-key sequences; controller wiring lives in src/tui/controller.ts and registers chords such as Ctrl-W + .
  • Tests: Several TUI tests exist (tests/tui/*) but there is currently no reproducible test that demonstrates the freeze.

Desired change

  • Collect diagnostic traces from affected environments (WSL + Windows Terminal reported) including: TUI debug logs, chord debug output (TUI_CHORD_DEBUG=1), and system call traces (strace) or equivalent.
  • Using traces, determine whether the freeze is caused by: event-loop starvation, a long-running synchronous syscall, chord handler timer misbehavior, duplicate key events, or external blocking I/O.
  • Implement a minimal fix (bugfix or guard) or mitigation and add regression test(s) and improved telemetry/logging so future occurrences are easier to triage.

Related work

Potentially related docs

  • src/tui/chords.ts — ChordHandler implementation (handles leader key state, timeout, duplicate-key handling).
  • src/tui/controller.ts — TUI controller: wiring of screen, chord registration, and raw keypress handling; includes uses of the chord system and places where input is consumed.
  • src/tui/constants.ts — Centralized TUI constants and shortcut definitions; useful to confirm which shortcuts are registered.

Potentially related work items

  • Refactor TUI keyboard handler into reusable chord system (WL-0ML04S0SZ1RSMA9F) — completed; may contain historical context for chord design.
  • Create keyboard chord handler module (WL-0MLI8KZF418JW4QB) — completed; likely earlier iteration of chords implementation.
  • Update dialog keyboard registers triple keypresses (WL-0MLFSF2AI0CSG478) — completed; may contain fixes for duplicate key events handling.
  • Fix navigation in update window (WL-0MLBS41JK0NFR1F4) — completed; contains navigation/keyboard behaviour changes that might be relevant.

Requested information (to complete triage)

Please provide the following when available (these will directly enable the next triage steps):

  1. Exact environment strings: WSL distro+version, Windows version, Windows Terminal version, and the app version/commit you are running.
  2. Exact command you use to start the TUI in WSL (e.g. npm run tui or ./bin/opencode tui).
  3. Capture diagnostics when a freeze occurs and attach the outputs: TUI stdout/stderr, TUI_CHORD_DEBUG logs, and an strace of the process (or an asciinema recording). Example commands are provided below.

Immediate triage commands (copy/paste for WSL)

  • Start with chord debug and verbose logs:
    TUI_CHORD_DEBUG=1 npm run tui # or your TUI start command
  • If running under Node on WSL, capture syscalls duri...

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix TUI unresponsive to keyboard input Prevent intermittent TUI input stalls by expiring deduplicated chord prefixes Mar 13, 2026
Copilot AI requested a review from SorraTheOrc March 13, 2026 08:17
@SorraTheOrc
Copy link
Member

@copilot Look for other potential causes, I don't think this is the root cause of this specific problem. Leave this fix in place, however.

Copilot AI and others added 2 commits March 13, 2026 09:09
Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot Look for other potential causes, I don't think this is the root cause of this specific problem. Leave this fix in place, however.

I investigated additional causes and kept the prior chord timeout fix in place. I found another plausible freeze path: the opencode textarea can keep screen.grabKeys engaged after focus moves away, which suppresses global key handlers. I added cleanup to release grabKeys on Ctrl-W pane switches and opencode close/escape paths, plus a regression test for Ctrl-W then k focus switching (test/tui-integration.test.ts). Addressed in commit e0f447e (follow-up on 9445d3a). Screenshot: /tmp/playwright-logs/page-2026-03-13T09-09-45-652Z.png.

Copilot AI changed the title Prevent intermittent TUI input stalls by expiring deduplicated chord prefixes Prevent intermittent TUI input stalls from stuck chord pending state and opencode grabKeys leakage Mar 13, 2026
@SorraTheOrc
Copy link
Member

@copilot I still don't think that's the problem, though you should keep that fix too.

This issue happens sometimes with no interaction from the user. Are there any periodic processes that run automatically in the TUI that might cause this?

Copilot AI and others added 2 commits March 13, 2026 10:41
Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot I still don't think that's the problem, though you should keep that fix too.

This issue happens sometimes with no interaction from the user. Are there any periodic processes ...

Yes—there is an automatic background process in the TUI: a filesystem watcher (fs.watch) on the worklog data directory that schedules refreshes. I found a plausible stall path when the watcher emits events without a filename (common on some platforms), which can cause refresh churn from unrelated directory activity even with no user interaction. I kept prior fixes and added a targeted guard so filename-less events only refresh when the JSONL data file mtime actually changes, with regression coverage for unchanged mtime, changed mtime, and stat failures. Addressed in 9c7daf8 and refined in f47ca6f. Screenshot: /tmp/playwright-logs/page-2026-03-13T10-40-34-632Z.png.

Copilot AI changed the title Prevent intermittent TUI input stalls from stuck chord pending state and opencode grabKeys leakage Prevent intermittent TUI input stalls from chord pending state, opencode grabKeys leakage, and watcher refresh churn Mar 13, 2026
Copilot AI and others added 6 commits March 13, 2026 07:49
- Add Risk and Effort rows to MetadataPaneComponent (with '—' placeholder when empty)
- Always show Risk/Effort in CLI concise/normal/full/tree formats (with '—' placeholder)
- Update tests: row count 9→11, add risk/effort rendering assertions
- Add new tests for placeholder and value rendering in TUI and CLI helpers
- Update CLI.md and TUI.md docs to mention Risk/Effort always visible

Co-authored-by: SorraTheOrc <250240+SorraTheOrc@users.noreply.github.com>
…and prefer blessed APIs for grabKeys cleanup
…atSync, ignore transient stat failures (fixes flaky tui integration test)
@SorraTheOrc SorraTheOrc marked this pull request as ready for review March 13, 2026 15:12
@SorraTheOrc SorraTheOrc merged commit c43cc06 into main Mar 13, 2026
4 checks passed
@SorraTheOrc SorraTheOrc deleted the copilot/fix-tui-unresponsive-keyboard-input branch March 21, 2026 16:22
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.

TUI unresponsive to keyboard input

2 participants