From ff7a2873f9b1f63f1609a00d32afdb2265de9857 Mon Sep 17 00:00:00 2001 From: Godopu Date: Sun, 19 Jul 2026 20:05:48 +0900 Subject: [PATCH] Implement full E2E, integration, component, and unit tests, and resolve all leftover tmux-to-herdr issues in UI and core scripts --- .agents/skills/lib.sh | 142 +++- .../multi-agent-mux-delegate-job | 60 +- .../scripts/job_subscriber.py | 6 +- .../multi-agent-mux-loop/scripts/run_loop.sh | 4 +- .../scripts/reconcile.sh | 25 +- .../scripts/resume_session.sh | 3 +- .../scripts/update_yaml_resumed.sh | 3 +- .../multi-agent-mux-status/scripts/status.sh | 4 +- .../lib/src/widgets/detail_pane.dart | 2 +- .../lib/src/widgets/session_table.dart | 8 +- .../lib/src/widgets/terminal_pane.dart | 4 +- .../apps/mam_desktop/test/widget_test.dart | 4 +- .../test/incremental_kernel.Ly9AZGFydD0zLjA= | Bin 4028768 -> 4028312 bytes .../mam_core/lib/src/models/pane.dart | 2 +- .../mam_core/lib/src/models/session_row.dart | 7 +- .../lib/src/models/sessions_snapshot.dart | 17 +- .../lib/src/services/session_service.dart | 6 +- .../mam_core/test/session_service_test.dart | 21 +- .../test/incremental_kernel.Ly9AZGFydD0zLjA= | Bin 4060680 -> 4060872 bytes .../packages/mam_pty/lib/src/pty_session.dart | 4 +- TEST_INFRA.md | 49 ++ TEST_READY.md | 129 +++ tests/conftest.py | 516 ++++++++++++ tests/test_challenger_m2.py | 182 +++++ tests/test_sanity.py | 74 ++ tests/test_tier1_unit.py | 363 +++++++++ tests/test_tier2_component.py | 734 ++++++++++++++++++ tests/test_tier3_integration.py | 401 ++++++++++ tests/test_tier4_e2e.py | 424 ++++++++++ 29 files changed, 3099 insertions(+), 95 deletions(-) create mode 100644 TEST_INFRA.md create mode 100644 TEST_READY.md create mode 100644 tests/conftest.py create mode 100644 tests/test_challenger_m2.py create mode 100644 tests/test_sanity.py create mode 100644 tests/test_tier1_unit.py create mode 100644 tests/test_tier2_component.py create mode 100644 tests/test_tier3_integration.py create mode 100644 tests/test_tier4_e2e.py diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index 42a5b75..af39f2c 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -28,7 +28,7 @@ AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions. # Add common Homebrew and local binary paths to PATH to ensure they are available in non-interactive shells for dir in /home/linuxbrew/.linuxbrew/bin /home/linuxbrew/.linuxbrew/sbin "$HOME/.local/bin" "$HOME/.npm-global/bin"; do if [ -d "$dir" ] && [[ ":$PATH:" != *":$dir:"* ]]; then - export PATH="$dir:$PATH" + export PATH="$PATH:$dir" fi done @@ -58,7 +58,16 @@ _resolve_real_herdr_path() { _init_herdr_isolation() { local wrapper_dir="$WORKSPACE_ROOT/.mam/shim" mkdir -p "$wrapper_dir" - cat <<'EOF' > "$wrapper_dir/herdr" + if [ -x "$wrapper_dir/herdr" ]; then + if [[ ":$PATH:" != *":$wrapper_dir:"* ]]; then + export PATH="$wrapper_dir:$PATH" + fi + return 0 + fi + + local tmp_file + tmp_file=$(mktemp "$wrapper_dir/herdr.XXXXXX") + cat <<'EOF' > "$tmp_file" #!/usr/bin/env bash # Herdr-to-Herdr translation shim wrapper set -euo pipefail @@ -81,6 +90,16 @@ _resolve_real_herdr() { } REAL_HERDR=$(_resolve_real_herdr) +# Support parsing -L before the subcommand +while [ "${1:-}" = "-L" ]; do + if [ $# -lt 2 ]; then + echo "herdr shim: -L requires an argument" >&2 + exit 1 + fi + export HERDR_SERVER_NAME="$2" + shift 2 +done + wrapper_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) cmd="${1:-}" if [ -z "$cmd" ]; then @@ -89,12 +108,20 @@ if [ -z "$cmd" ]; then fi shift + case "$cmd" in has-session) sess="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; *) shift ;; esac done @@ -104,12 +131,29 @@ case "$cmd" in name="" ws="" run_cmd="" while [ $# -gt 0 ]; do case "$1" in - -s) name="$2"; shift 2 ;; - -c) ws="$2"; shift 2 ;; + -s) + if [ $# -lt 2 ]; then + echo "Error: -s requires a value" >&2 + exit 1 + fi + name="$2" + shift 2 + ;; + -c) + if [ $# -lt 2 ]; then + echo "Error: -c requires a value" >&2 + exit 1 + fi + ws="$2" + shift 2 + ;; -d|-x|-y) shift ;; *) run_cmd="$1"; shift ;; esac done + if [ -z "$run_cmd" ]; then + run_cmd="${SHELL:-/bin/bash}" + fi "$REAL_HERDR" workspace create --label "${HERDR_SERVER_NAME:-default}" --cwd "${ws:-.}" >/dev/null 2>&1 || true parsed=$(python3 -c " @@ -136,13 +180,14 @@ print('\t'.join(env_flags) + '\n' + ' '.join(binary_tokens)) ws_label="${HERDR_SERVER_NAME:-default}" ws_id="w1" if [ "$ws_label" != "default" ]; then - ws_id=$("$REAL_HERDR" workspace list 2>/dev/null | python3 -c " -import sys, json + ws_id=$("$REAL_HERDR" workspace list 2>/dev/null | WS_LABEL="$ws_label" python3 -c " +import sys, os, json try: + ws_label = os.environ.get('WS_LABEL', '') data = json.loads(sys.stdin.read()) res = data.get('result', data) for ws in res.get('workspaces', []): - if ws.get('label') == '$ws_label' or ws.get('workspace_id') == '$ws_label': + if ws.get('label') == ws_label or ws.get('workspace_id') == ws_label: print(ws.get('workspace_id')) sys.exit(0) except Exception: @@ -157,7 +202,14 @@ except Exception: sess="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; *) shift ;; esac done @@ -168,8 +220,22 @@ except Exception: sess="" format="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; - -F) format="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; + -F) + if [ $# -lt 2 ]; then + echo "Error: -F requires a value" >&2 + exit 1 + fi + format="$2" + shift 2 + ;; *) shift ;; esac done @@ -198,7 +264,14 @@ except Exception: sess="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; *) shift ;; esac done @@ -208,7 +281,14 @@ except Exception: sess="" key="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; *) key="$1"; shift ;; esac done @@ -221,7 +301,14 @@ except Exception: text="" while [ $# -gt 0 ]; do case "$1" in - -b) buf="$2"; shift 2 ;; + -b) + if [ $# -lt 2 ]; then + echo "Error: -b requires a value" >&2 + exit 1 + fi + buf="$2" + shift 2 + ;; *) text="$1"; shift ;; esac done @@ -231,7 +318,14 @@ except Exception: sess="" while [ $# -gt 0 ]; do case "$1" in - -t) sess="$2"; shift 2 ;; + -t) + if [ $# -lt 2 ]; then + echo "Error: -t requires a value" >&2 + exit 1 + fi + sess="$2" + shift 2 + ;; *) shift ;; esac done @@ -249,7 +343,11 @@ try: data = json.load(sys.stdin) res = data.get('result', data) for a in res.get('agents', []): - print(f\"{a['name']}|0\") + try: + name = a.get('name') or a.get('agent') or 'unknown' + print(f\"{name}|0\") + except Exception: + pass except Exception: pass " || true @@ -259,7 +357,8 @@ except Exception: ;; esac EOF - chmod +x "$wrapper_dir/herdr" + chmod +x "$tmp_file" + mv -f "$tmp_file" "$wrapper_dir/herdr" if [[ ":$PATH:" != *":$wrapper_dir:"* ]]; then export PATH="$wrapper_dir:$PATH" fi @@ -334,7 +433,7 @@ name = os.environ['SESSION_NAME'] d = json.loads(os.environ.get('MAM_STATE_JSON', '{}')) for s in d.get('herdr_sessions', []): if s.get('name') == name: - print(s.get('herdr_workspace', 'default')) + print(s.get('herdr_workspace') or s.get('herdr_server') or 'default') sys.exit(0) print(os.environ.get('HERDR_SERVER_NAME', 'default')) ") @@ -342,13 +441,14 @@ print(os.environ.get('HERDR_SERVER_NAME', 'default')) echo "w1" else local ws_id - ws_id=$(herdr workspace list 2>/dev/null | python3 -c " -import sys, json + ws_id=$(herdr workspace list 2>/dev/null | RAW_LABEL="$raw_label" python3 -c " +import sys, os, json try: + raw_label = os.environ.get('RAW_LABEL', '') data = json.loads(sys.stdin.read()) res = data.get('result', data) for ws in res.get('workspaces', []): - if ws.get('label') == '$raw_label' or ws.get('workspace_id') == '$raw_label': + if ws.get('label') == raw_label or ws.get('workspace_id') == raw_label: print(ws.get('workspace_id')) sys.exit(0) except Exception: diff --git a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job index bb78cfe..253d5cc 100755 --- a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job +++ b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job @@ -11,7 +11,7 @@ # # This is a reference wrapper: it shells out to the python scripts that live # next to it. Copy it into your project and customise as needed. It never hard -# fails if `claude`/`codex`/`tmux` are missing — it prints what it would run. +# fails if `claude`/`codex`/`herdr` are missing — it prints what it would run. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -28,6 +28,8 @@ pick_python() { local py_bin if [[ -n "${DELEGATE_JOB_PYTHON:-}" ]]; then py_bin="$DELEGATE_JOB_PYTHON" + elif [[ -n "${AGENT_PYTHON_BIN:-}" ]] && [[ -x "$AGENT_PYTHON_BIN" ]]; then + py_bin="$AGENT_PYTHON_BIN" elif [[ -x "${WORKDIR:-.}/.venv/bin/python" ]]; then py_bin="${WORKDIR}/.venv/bin/python" elif [[ -x ".venv/bin/python" ]]; then @@ -56,7 +58,7 @@ multi-agent-mux-delegate-job [options] [--type ] [--reviewer ] [--reviewer-session ] [--max-iterations ] [--counterpart-role ] [--strict-role-check] - # The skill is tmux-interactive only; --mode print was removed. + # The skill is herdr-interactive only; --mode print was removed. status --job [--registry-dir ] list [--registry-dir ] verify --job --validate