From 97d49898b7d0c989b8845968c7d6c27446d2b150 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 8 May 2026 20:37:40 +0200 Subject: [PATCH] fix(paliadin): 200ms settle delay between paste and Enter so submit registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit m's dogfood 2026-05-08 20:35: "the paliadin hook does not always work — it does not confirm the claude / terminal command... like lacking an enter key. Or too fast." Race between two consecutive tmux send-keys calls: the first writes the prompt literally with `-l`; the second sends an Enter key event. Claude Code's TUI debounces keyboard input. When the Enter lands while the paste is still being absorbed, the carriage-return collapses into the input buffer as a literal newline character instead of registering as a "submit" gesture — the prompt sits typed but unsubmitted, and the backend's pollForResponse then times out on the missing response file. Fix: sleep 200ms between the literal paste and the Enter. Below the human-perceptible threshold but well above tmux's pty flush window and the TUI's input-debounce window. Applied to both code paths: - scripts/paliadin-shim:send_to_pane (the SSH/RPC production path) - internal/services/paliadin.go:LocalPaliadinService.sendToPane (the laptop-only direct-tmux path) The Go-side variant uses a context-aware sleep so request cancellation still propagates correctly. Production shim copy at /home/m/.local/bin/paliadin-shim refreshed locally on mRiver so the next turn picks up the fix without waiting for redeploy. (The Dokploy container does not run paliadin — gate on PaliadinOwnerEmail is owner-only and prod has no claude+tmux anyway — so no deploy step required for the shim path.) --- internal/services/paliadin.go | 13 +++++++++++++ scripts/paliadin-shim | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/internal/services/paliadin.go b/internal/services/paliadin.go index 9001b61..54a7590 100644 --- a/internal/services/paliadin.go +++ b/internal/services/paliadin.go @@ -668,6 +668,19 @@ func (s *LocalPaliadinService) sendToPane(ctx context.Context, target, msg strin if err := runTmux(ctx, "send-keys", "-t", target, "-l", msg); err != nil { return err } + // Settle delay between the literal paste and the Enter. Claude Code's + // TUI debounces keyboard input; if Enter lands while the paste is + // still being absorbed, the carriage-return collapses into the input + // buffer as a literal newline character instead of registering as a + // "submit" gesture, leaving the prompt typed but unsubmitted (m's + // dogfood 2026-05-08 20:35: "lacking an enter key... or too fast"). + // 200ms is below the human-perceptible threshold but well above + // tmux's pty flush window. Mirrors scripts/paliadin-shim:send_to_pane. + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(200 * time.Millisecond): + } // Trailing Enter. tmux send-keys treats "Enter" as a special key name. if err := runTmux(ctx, "send-keys", "-t", target, "Enter"); err != nil { return err diff --git a/scripts/paliadin-shim b/scripts/paliadin-shim index dd1951a..922ee30 100755 --- a/scripts/paliadin-shim +++ b/scripts/paliadin-shim @@ -126,9 +126,19 @@ ensure_pane() { } # send_to_pane writes a literal string then Enter. +# +# Settle delay between the literal paste and the Enter: Claude Code's +# TUI debounces keyboard input; if Enter lands while the paste is still +# being absorbed, the carriage-return collapses into the input buffer +# as a literal newline character instead of registering as a "submit" +# gesture, leaving the prompt typed but unsubmitted (m's dogfood +# 2026-05-08 20:35: "lacking an enter key... or too fast"). 200ms is +# below the human-perceptible threshold but well above tmux's pty flush +# window. send_to_pane() { local target="$1" msg="$2" tmux send-keys -t "$target" -l -- "$msg" + sleep 0.2 tmux send-keys -t "$target" Enter }