fix(lib): handle agent_not_ready startup state and reject claude fullscreen upsell modal
- Accept agent_not_ready from herdr agent start to allow startup dialog handling without premature rollback, while preserving fail-closed behavior on dead process timeouts. - Match Claude fullscreen renderer upsell modal via 'Yes, try it' and dismiss with Escape to avoid dropping permission flags or deadlocking on idle /tui tips. - Add behavioral test suite in test_b19_headless_reconcile_fixes.py and cross-agent review reports.
This commit is contained in:
@@ -3,6 +3,7 @@ import sys
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from lib_py.layout import compute_2xk_layout
|
||||
@@ -101,6 +102,111 @@ def test_bug4_send_keys_safe_gating_order():
|
||||
assert dialog_idx < prompt_idx, "_pane_dialog_open must execute before agent prompt fast-path"
|
||||
|
||||
|
||||
_LIB_SH = str(Path(__file__).resolve().parent.parent / ".agents" / "skills" / "lib.sh")
|
||||
|
||||
_FULLSCREEN_TIP = """Claude Code
|
||||
Try the new fullscreen renderer — flicker-free output, mouse support, auto-copy on select · /tui fullscreen
|
||||
❯
|
||||
"""
|
||||
|
||||
_FULLSCREEN_MODAL = """Try the new fullscreen renderer?
|
||||
Flicker-free output
|
||||
Selected text auto-copies to your clipboard
|
||||
Yes, try it
|
||||
"""
|
||||
|
||||
|
||||
def _run_lib_helpers(script_body, env_extra=None):
|
||||
env = dict(os.environ)
|
||||
if env_extra:
|
||||
env.update(env_extra)
|
||||
wrapper = f"""
|
||||
set -euo pipefail
|
||||
source "{_LIB_SH}"
|
||||
_init_herdr_isolation() {{ :; }}
|
||||
{script_body}
|
||||
"""
|
||||
return subprocess.run(["bash", "-c", wrapper], capture_output=True, text=True, env=env)
|
||||
|
||||
|
||||
def test_agent_start_success_tokens_exclude_startup_timeout():
|
||||
"""D-1: dead-process timeout must not be promoted to success; agent_not_ready may."""
|
||||
content = open(_LIB_SH, encoding="utf-8").read()
|
||||
success_line = next(l for l in content.splitlines()
|
||||
if 'grep -qE "agent_started' in l)
|
||||
assert "agent_not_ready" in success_line
|
||||
assert "timed out waiting for agent startup" not in success_line
|
||||
err_line = next(i for i, l in enumerate(content.splitlines())
|
||||
if 'grep -qiE "^usage:' in l)
|
||||
ok_line = next(i for i, l in enumerate(content.splitlines())
|
||||
if 'grep -qE "agent_started' in l)
|
||||
assert err_line < ok_line
|
||||
|
||||
|
||||
def test_fullscreen_tip_is_not_a_blocking_dialog():
|
||||
"""D-2: idle /tui fullscreen tip must not trip _pane_dialog_open or send keys."""
|
||||
script = f"""
|
||||
_pane_capture() {{ printf '%s' '{_FULLSCREEN_TIP}'; }}
|
||||
KEYS=/tmp/mam-fs-tip-keys.$$
|
||||
: > "$KEYS"
|
||||
_sks_herdr() {{
|
||||
if [ "${{1:-}}" = "send-keys" ]; then echo "$*" >> "$KEYS"; fi
|
||||
return 0
|
||||
}}
|
||||
if _pane_dialog_open dummy; then echo "DIALOG_OPEN"; else echo "DIALOG_CLOSED"; fi
|
||||
handle_startup_dialogs dummy 2
|
||||
echo "KEYS_CONTENT=$(tr '\\n' '|' < "$KEYS")"
|
||||
rm -f "$KEYS"
|
||||
"""
|
||||
res = _run_lib_helpers(script)
|
||||
assert res.returncode == 0, res.stderr + res.stdout
|
||||
assert "DIALOG_CLOSED" in res.stdout
|
||||
assert "Escape" not in res.stdout
|
||||
assert "Enter" not in res.stdout.split("KEYS_CONTENT=")[-1]
|
||||
|
||||
|
||||
def test_fullscreen_modal_is_rejected_not_accepted():
|
||||
"""D-3: Yes, try it modal is a dialog; dismiss with Escape, never Enter."""
|
||||
script = f"""
|
||||
_pane_capture() {{ printf '%s' '{_FULLSCREEN_MODAL}'; }}
|
||||
KEYS=/tmp/mam-fs-modal-keys.$$
|
||||
: > "$KEYS"
|
||||
_sks_herdr() {{
|
||||
if [ "${{1:-}}" = "send-keys" ]; then echo "$*" >> "$KEYS"; fi
|
||||
return 0
|
||||
}}
|
||||
if _pane_dialog_open dummy; then echo "DIALOG_OPEN"; else echo "DIALOG_CLOSED"; fi
|
||||
handle_startup_dialogs dummy 1
|
||||
echo "KEYS_CONTENT=$(tr '\\n' '|' < "$KEYS")"
|
||||
rm -f "$KEYS"
|
||||
"""
|
||||
res = _run_lib_helpers(script)
|
||||
assert res.returncode == 0, res.stderr + res.stdout
|
||||
assert "DIALOG_OPEN" in res.stdout
|
||||
keys = res.stdout.split("KEYS_CONTENT=")[-1]
|
||||
assert "Escape" in keys
|
||||
assert "Enter" not in keys
|
||||
|
||||
|
||||
def test_wait_for_tui_ready_succeeds_on_fullscreen_tip():
|
||||
"""D-2c: tip + ready banner must not deadlock wait_for_tui_ready."""
|
||||
script = f"""
|
||||
_pane_capture() {{ printf '%s' '{_FULLSCREEN_TIP}'; }}
|
||||
_sks_herdr() {{
|
||||
if [ "${{1:-}}" = "capture-pane" ]; then printf '%s' '{_FULLSCREEN_TIP}'; return 0; fi
|
||||
if [ "${{1:-}}" = "send-keys" ]; then echo "KEY:$*" >&2; return 0; fi
|
||||
return 0
|
||||
}}
|
||||
sleep() {{ :; }}
|
||||
export MAM_READY_TOKENS='Claude Code|Welcome'
|
||||
wait_for_tui_ready dummy-sess claude
|
||||
"""
|
||||
res = _run_lib_helpers(script)
|
||||
assert res.returncode == 0, res.stderr + res.stdout
|
||||
assert "TUI detected ready" in res.stdout
|
||||
assert "KEY:" not in res.stderr
|
||||
|
||||
|
||||
def test_bug4_no_duplicate_input_on_rpc_success(tmp_path):
|
||||
"""Verify Bug 4: when herdr agent prompt succeeds, send_keys_safe returns 0 without calling paste-buffer."""
|
||||
test_script = f"""#!/usr/bin/env bash
|
||||
|
||||
Reference in New Issue
Block a user