refactor(uuid): resolve B-10 by deprecating agent_identities and removing PyYAML dependency
- Remove dead agent_identities read path and PyYAML import from workspace_uuid.py - Defer eager PyYAML import in verify_session.py to lazy YAML fallback branch - Simplify UUID resolution to 2-tier model (tier-1 own row ID -> tier-2 adapter scan) - Clean up unused Drift D and ghost cache clearing in reconcile.sh and stop_session.sh - Add 3 regression guards in tests/test_tier1_unit.py (266/266 PASS) - Update IMPROVEMENTS.md, VERSIONS.md, and SKILL.md files
This commit is contained in:
@@ -338,3 +338,67 @@ def test_mqtt_with_retry_failure(mam_sandbox):
|
||||
with pytest.raises(ValueError, match="failing"):
|
||||
failing_func()
|
||||
assert len(calls) == 3
|
||||
|
||||
|
||||
def test_b10_no_agent_identities_reader_in_production():
|
||||
"""B-10: agent_identities has no writer; no production code may read it."""
|
||||
import pathlib
|
||||
root = pathlib.Path(__file__).resolve().parent.parent
|
||||
targets = [
|
||||
root / ".agents" / "skills" / "lib_py" / "workspace_uuid.py",
|
||||
root / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh",
|
||||
root / ".agents" / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh",
|
||||
root / ".agents" / "skills" / "lib.sh",
|
||||
]
|
||||
offenders = []
|
||||
for f in targets:
|
||||
for i, line in enumerate(f.read_text().splitlines(), 1):
|
||||
if "agent_identities" not in line:
|
||||
continue
|
||||
if line.lstrip().startswith("#"): # 금지 규약을 서술하는 주석은 허용
|
||||
continue
|
||||
offenders.append(f"{f.name}:{i}: {line.strip()}")
|
||||
assert not offenders, "agent_identities read path resurrected:\n" + "\n".join(offenders)
|
||||
|
||||
|
||||
def test_b10_workspace_uuid_has_no_yaml_import():
|
||||
"""B-10: no `import yaml` anywhere in workspace_uuid.py — top-level OR lazy."""
|
||||
import ast, pathlib
|
||||
src = (pathlib.Path(__file__).resolve().parent.parent
|
||||
/ ".agents" / "skills" / "lib_py" / "workspace_uuid.py")
|
||||
tree = ast.parse(src.read_text())
|
||||
offenders = []
|
||||
for node in ast.walk(tree): # ast.walk → 중첩 깊이 무관
|
||||
if isinstance(node, ast.Import):
|
||||
for a in node.names:
|
||||
if a.name.split(".")[0] == "yaml":
|
||||
offenders.append(f"line {node.lineno}: import {a.name}")
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if (node.module or "").split(".")[0] == "yaml":
|
||||
offenders.append(f"line {node.lineno}: from {node.module} import ...")
|
||||
assert not offenders, "PyYAML dependency reintroduced:\n" + "\n".join(offenders)
|
||||
|
||||
|
||||
def test_b10_find_workspace_uuid_runs_without_pyyaml(tmp_path):
|
||||
"""B-10: the executed resolution path must not need PyYAML."""
|
||||
import subprocess, sys, os, json, pathlib
|
||||
stub = tmp_path / "noyaml"
|
||||
(stub / "yaml").mkdir(parents=True)
|
||||
(stub / "yaml" / "__init__.py").write_text('raise ImportError("PyYAML absent (stub)")\n')
|
||||
skills = str(pathlib.Path(__file__).resolve().parent.parent / ".agents" / "skills")
|
||||
ws = tmp_path / "ws"; ws.mkdir()
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = f"{stub}:{skills}"
|
||||
env["WS_ABS"] = str(ws)
|
||||
env["AGENT"] = "claude"
|
||||
env["MAM_STATE_JSON"] = json.dumps({"herdr_sessions": []})
|
||||
env["YAML_PATH"] = str(tmp_path / "agent-sessions.yaml")
|
||||
env["HOME_DIR"] = str(tmp_path)
|
||||
env["CLAUDE_PROJECT_DIR"] = str(tmp_path / "projects")
|
||||
r = subprocess.run(
|
||||
[sys.executable, "-c",
|
||||
"from lib_py.workspace_uuid import find_workspace_uuid_main; find_workspace_uuid_main()"],
|
||||
capture_output=True, text=True, env=env)
|
||||
assert r.returncode == 0, f"resolution path still needs PyYAML: {r.stderr}"
|
||||
assert "yaml" not in r.stderr.lower(), f"PyYAML touched at runtime: {r.stderr}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user