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:
2026-08-27 10:17:53 +09:00
parent d875584ef4
commit 4bbd03bf2d
5 changed files with 402 additions and 4 deletions
+106
View File
@@ -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