From 2bd59fce9b064ae569e69281afea2899d775eba1 Mon Sep 17 00:00:00 2001 From: Godopu Date: Thu, 13 Aug 2026 09:36:07 +0900 Subject: [PATCH] fix(lib): prevent set -u unbound variable error for final_cmd in kind detection and harden test fixtures --- .agents/skills/lib.sh | 6 +++--- tests/conftest.py | 33 ++++++++++++++++++++++++++------- tests/test_tier3_integration.py | 31 ++++++++++++++++++------------- tests/test_tier4_e2e.py | 3 +++ 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index 06c9fdd..1009167 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -271,11 +271,11 @@ print('\t'.join(env_flags) + '\n' + ' '.join(binary_tokens)) kind="claude" elif echo "$name" | grep -qi "hermes"; then kind="hermes" - elif echo "$final_cmd" | grep -qi "agy"; then + elif echo "${final_cmd:-}" | grep -qi "agy"; then kind="agy" - elif echo "$final_cmd" | grep -qi "claude"; then + elif echo "${final_cmd:-}" | grep -qi "claude"; then kind="claude" - elif echo "$final_cmd" | grep -qi "hermes"; then + elif echo "${final_cmd:-}" | grep -qi "hermes"; then kind="hermes" fi diff --git a/tests/conftest.py b/tests/conftest.py index 80ad1ad..0a47961 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,12 +87,12 @@ if not os.path.exists(state_file): sys.exit(0) # Lock the state file exclusively to prevent concurrent race conditions -lock_f = open(state_file + ".lock", "w") +lock_f = open(state_file + ".lock", "a") fcntl.flock(lock_f, fcntl.LOCK_EX) state = {"workspaces": [], "agents": {}, "calls": []} if os.path.exists(state_file): - for _retry in range(10): + for _retry in range(50): try: with open(state_file, 'r') as f: content = f.read().strip() @@ -100,16 +100,35 @@ if os.path.exists(state_file): state = json.loads(content) break except Exception: - import time - time.sleep(0.05) + pass + time.sleep(0.02) # Record the command call state["calls"].append(sys.argv[1:]) def save_state(): + disk_state = {"workspaces": [], "agents": {}, "calls": []} + if os.path.exists(state_file): + for _retry in range(50): + try: + with open(state_file, 'r') as f: + content = f.read().strip() + if content: + disk_state = json.loads(content) + break + except Exception: + pass + time.sleep(0.02) + disk_state["agents"] = state.get("agents", {}) + if "workspaces" in state: + disk_state["workspaces"] = state["workspaces"] + disk_calls = disk_state.setdefault("calls", []) + if sys.argv[1:] and (not disk_calls or disk_calls[-1] != sys.argv[1:]): + disk_calls.append(sys.argv[1:]) + tmp_state = state_file + f".tmp.{os.getpid()}" with open(tmp_state, 'w') as f: - json.dump(state, f, indent=2) + json.dump(disk_state, f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(tmp_state, state_file) @@ -242,7 +261,7 @@ elif cmd1 == "agent": "cwd": cwd or "TMP_PATH_PLACEHOLDER", "workspace_id": ws or "w1", "pid": 9999, - "pane_id": f"w1:p{len(agents)+1}", + "pane_id": f"w1:p_{name}", "command": " ".join(agent_cmd), "buffer": buffer_content } @@ -376,7 +395,7 @@ elif cmd1 == "agent": agents = state.get("agents", {}) if name in agents: agents[name]["sent_text"] = agents[name].get("sent_text", "") + text - if text == "C-m": + if text in ("C-m", "Enter"): agents[name]["buffer"] = agents[name].get("buffer", "") + "\\nesc to interrupt" else: agents[name]["buffer"] = agents[name].get("buffer", "") + "\\n" + text diff --git a/tests/test_tier3_integration.py b/tests/test_tier3_integration.py index ae615b1..1551098 100644 --- a/tests/test_tier3_integration.py +++ b/tests/test_tier3_integration.py @@ -2,6 +2,7 @@ import os import subprocess import json import sqlite3 +import fcntl import pytest import shutil import yaml @@ -117,19 +118,23 @@ def test_integration_stop_purge_combination(mam_sandbox, mock_herdr, mock_agents assert jsonl_file.exists() # Update mock herdr's state to match the running agent so the stop kill-chain works gracefully - with open(mock_herdr, 'r') as f: - state = json.load(f) - state["agents"][session_name] = { - "status": "running", - "agent": "claude", - "cwd": str(tmp_path), - "pid": 12345, - "pane_id": "w1:p1", - "command": "claude", - "buffer": "Anthropic Claude Ready" - } - with open(mock_herdr, 'w') as f: - json.dump(state, f, indent=2) + lock_path = str(mock_herdr) + ".lock" + with open(lock_path, 'a') as lock_f: + fcntl.flock(lock_f, fcntl.LOCK_EX) + with open(mock_herdr, 'r') as f: + state = json.load(f) + state["agents"][session_name] = { + "status": "running", + "agent": "claude", + "cwd": str(tmp_path), + "pid": 12345, + "pane_id": f"w1:p_{session_name}", + "command": "claude", + "buffer": "Anthropic Claude Ready" + } + with open(mock_herdr, 'w') as f: + json.dump(state, f, indent=2) + fcntl.flock(lock_f, fcntl.LOCK_UN) # 2. Calling stop_session without --yes fails for --purge-conversation stop_script = tmp_path / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh" diff --git a/tests/test_tier4_e2e.py b/tests/test_tier4_e2e.py index a9d5513..0789267 100644 --- a/tests/test_tier4_e2e.py +++ b/tests/test_tier4_e2e.py @@ -295,18 +295,21 @@ d['herdr_sessions'] = [ 'name': '{worker_name}', 'status': 'running', 'role': 'worker', + 'claude_session_id_own': '11111111-1111-4111-a111-111111111111', 'pane': {{'cwd': 'WS_PLACEHOLDER'}} }}, {{ 'name': '{reviewer_name}', 'status': 'running', 'role': 'reviewer', + 'claude_session_id_own': '22222222-2222-4222-a222-222222222222', 'pane': {{'cwd': 'WS_PLACEHOLDER'}} }}, {{ 'name': '{planner_name}', 'status': 'running', 'role': 'planner', + 'claude_session_id_own': '33333333-3333-4333-a333-333333333333', 'pane': {{'cwd': 'WS_PLACEHOLDER'}} }} ]