fix(paliadin): 200ms settle delay between paste and Enter so submit registers

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.)
This commit is contained in:
m
2026-05-08 20:37:40 +02:00
parent 5b08bfcb96
commit 97d49898b7
2 changed files with 23 additions and 0 deletions

View File

@@ -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

View File

@@ -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
}