Files
multi-agent-mux/.agents/reports/canary-projects-multi-agent-mux-creator-cline/report-98e3986f.md
T

12 KiB

Cross-Code Review: A-4 BaseAgentAdapter Introduction & Agent Knowledge Abstraction

Job ID: 98e3986f
Reviewer: cline
Date: 2026-08-14
Scope: A-4 BaseAgentAdapter 도입 및 에이전트 지식 추상화 — lint, operability, and loss cross-review


1. Change Summary

Modified Files (5)

File Change
lib.sh:1120-1125 F-1 fix: VERIFY_SESSION_PYTHON facade now fails explicitly (echo ERROR + return/exit 1) instead of silent empty string
lib_py/verify_session.py:82-86 home = home_dir or os.environ.get("HOME_DIR", "")home = resolve_home(home_dir)
lib_py/workspace_uuid.py:18-22 home = os.environ['HOME_DIR']home = resolve_home()
reconcile.sh:323,344-358,863-868 Replaces exec(os.environ['MAM_VERIFY_PY']) with direct from lib_py.verify_session import ...; replaces subprocess load_state_json with from lib_py.state import load_state_json (with subprocess fallback); removes MAM_VERIFY_PY="$VERIFY_SESSION_PYTHON" from both dry-run and write paths
deploy/gitea-ci.yml:76-78 py_compile glob changed from flat lib_py/*.py to recursive lib_py/**/*.py to catch new subdirectories

New Files (13)

File Purpose
lib_py/paths.py resolve_home() — unified HOME_DIR resolution contract (N0): explicit arg → HOME_DIR env → HOME env → expanduser('~'); raises ValueError on empty/root
lib_py/state.py load_state_json() — Python direct state loader for agent-sessions.yaml/.db (N7); faithful extraction of shell load_state_json
lib_py/agents/__init__.py Package init
lib_py/agents/base.py BaseAgentAdapter abstract base, SpawnSpec, DiscoveryContext (N4)
lib_py/agents/registry.py Static adapter registry: get_adapter(), own_key(), agent_of_row() (N4 & N5)
lib_py/agents/__main__.py CLI bridge: `python -m lib_py.agents <facts
lib_py/agents/adapters/__init__.py Adapters package init
lib_py/agents/adapters/claude.py ClaudeAgentAdapterown_key = claude_session_id_own
lib_py/agents/adapters/agy.py AgyAgentAdapterown_key = agy_conversation_id_own
lib_py/agents/adapters/hermes.py HermesAgentAdapterown_key = hermes_conversation_id_own
lib_py/agents/adapters/cline.py ClineAgentAdapterown_key = cline_conversation_id_own
tests/test_a4_adapter_contract.py 3 contract tests: resolve_home contract, adapter registry, agent_of_row priority

2. Syntax & Compilation Checks

Check Result
bash -n lib.sh PASS
bash -n reconcile.sh PASS
py_compile all lib_py/**/*.py (recursive, 11 files) PASS
py_compile tests/test_a4_adapter_contract.py PASS

3. Import & Module Verification

Check Result
from lib_py.paths import resolve_home OK
from lib_py.state import load_state_json OK
from lib_py.agents.registry import get_adapter, own_key, agent_of_row OK
from lib_py.agents.base import BaseAgentAdapter, SpawnSpec, DiscoveryContext OK
All 4 adapter imports (claude, agy, hermes, cline) OK
Circular import check None — base.pypaths, verify_session; registry.pybase + adapters; adapters → base only

CLI Bridge Verification

$ python -m lib_py.agents facts claude
AGENT_NAME=claude
OWN_KEY=claude_session_id_own

$ python -m lib_py.agents resolve my-workspace-creator-hermes
hermes

Registry Edge Cases

  • own_key('unknown')None
  • own_key('')None
  • get_adapter('CLAUDE') → case-insensitive → claude

4. Test Results

Test Suite Tests Result
test_a4_adapter_contract.py (new) 3 All PASS
test_sanity.py 2 All PASS
test_tier1_unit.py 29 All PASS
test_orc_onboard.py 40 All PASS
Total 74 All PASS

New contract tests verify:

  1. resolve_home contract: explicit arg, HOME_DIR env, HOME/expanduser fallback
  2. Adapter registry: all 4 agents have correct name and own_key (claude → session, others → conversation)
  3. agent_of_row priority: explicit agent field > session name suffix > pane.cmd match

5. Design Analysis

5.1 resolve_home() Contract (N0)

Well-designed unified home resolution with fail-close semantics:

  • Priority chain: explicit arg → HOME_DIR env → HOME env → expanduser('~')
  • Fail-close: raises ValueError if result is empty or / — prevents root-relative silent fail-close
  • Behavioral change in verify_session.py: Old code home_dir or os.environ.get("HOME_DIR", "") silently returned "" → now raises ValueError. This is the intended fix — callers that previously got "" would build paths like /.claude/projects (root-relative), which is a silent failure mode.
  • Behavioral improvement in workspace_uuid.py: Old code os.environ['HOME_DIR'] raised KeyError on missing env → now falls back to HOME/expanduser. More robust.

5.2 BaseAgentAdapter & Registry (N4 & N5)

Clean OOP design:

  • BaseAgentAdapter defines name, own_key (abstract properties), derive_session_name(), matches_session_name(), verify_session()
  • matches_session_name() checks -{role}-{name} suffix (creator/planner/reviewer) or bare -{name} — matches the kind detection logic in lib.sh
  • derive_session_name() returns f"{slug}-{role}-{name}" — matches the shell derive_session_name function
  • agent_of_row() implements strict priority: explicit agent field → session name suffix → pane.cmd exact/binary-path match → None
  • Static registry with 4 singleton adapter instances — no dynamic registration needed

5.3 state.py Extraction (N7)

Faithful extraction of shell load_state_json (lib.sh:828-869):

  • Identical DB-first, YAML-fallback logic
  • Identical clean_surrogates recursive cleaner
  • Identical sqlite3.connect(timeout=60.0) + PRAGMA busy_timeout = 60000
  • Enhancement: accepts yaml_path parameter with env var fallback (AGENT_SESSIONS_YAML or YAML_PATH) — more flexible than shell version which always gets YAML_PATH from env_python
  • Difference: returns dict directly instead of printing JSON to stdout — correct for in-process use

5.4 reconcile.sh Decoupling

  • exec(os.environ['MAM_VERIFY_PY'])from lib_py.verify_session import verify_session_uuid, workspace_key — eliminates the env-var code injection pattern
  • MAM_VERIFY_PY="$VERIFY_SESSION_PYTHON" removed from both dry-run and write paths — no more env-var coupling
  • load_state_json now tries direct Python import first, falls back to subprocess — performance improvement with safe fallback

5.5 CI glob fix (gitea-ci.yml)

Old: py_compile .agents/skills/lib_py/*.py (flat — misses lib_py/agents/**/*.py)
New: glob.glob('.agents/skills/lib_py/**/*.py', recursive=True) — correctly catches all nested Python files. Necessary change.


6. Findings

F-1 (Low): Unused imports in base.py and __main__.py

  • base.py:3: import os, json, sqlite3 — none used in the file body
  • base.py:4: from typing import Optional, Dict, Any, ListList unused
  • __main__.py:3: import sys, jsonjson unused
  • Impact: Dead imports; would be flagged by flake8 F401. No runtime impact.
  • Recommendation: Remove unused imports for cleanliness.

F-2 (Low): VERIFY_SESSION_PYTHON is now a dead variable

  • lib.sh:1121 defines VERIFY_SESSION_PYTHON="$(cat ...)" but no consumer remains — reconcile.sh was the only consumer (via MAM_VERIFY_PY), now removed.
  • The if-else block still serves as a file existence guard (else branch fails explicitly), but the variable assignment itself is dead code.
  • Impact: None at runtime — the guard is useful, the variable is harmless dead code.
  • Recommendation: Could simplify to a pure existence check, but keeping it documents the historical facade pattern. Acceptable as-is.

F-3 (Low): reconcile.sh:326 still uses os.environ['HOME_DIR'] directly

  • Line 326: home = os.environ['HOME_DIR'] — not changed to resolve_home()
  • reconcile.sh always sets HOME_DIR="$HOME_DIR" via shell wrapper (line 71), so KeyError is impossible in practice
  • Impact: Inconsistency with the resolve_home() pattern adopted in verify_session.py and workspace_uuid.py. No runtime risk.
  • Recommendation: Could adopt resolve_home() for consistency, but not required since the env var is always set by the wrapper.

F-4 (Low): test_resolve_home_contract test 3 fragility

  • Test 3 creates env_copy with HOME_DIR popped, computes expected home_val from the copy, but calls resolve_home() with the actual os.environ (which may still have HOME_DIR set from prior tests or environment).
  • If HOME_DIR was set in the environment when the test runs, resolve_home() would return the HOME_DIR value, but home_val would be HOME/expanduser — mismatch → test failure.
  • Impact: Test passes in current environment (HOME_DIR not set during direct pytest run), but is fragile in environments where HOME_DIR is pre-set.
  • Recommendation: Use monkeypatch.delenv('HOME_DIR', raising=False) to properly isolate the test.

7. Loss / Regression Analysis

Concern Status
MAM_VERIFY_PY references in codebase Fully removed from all .sh/.py files (only in .mam/jobs/ probe scripts — historical)
load_state_json shell function still used by other scripts Yes — stop_session.sh, orc_onboard.sh, run_loop.sh, lib.sh itself. The new state.py module coexists; only reconcile.sh uses the Python import path. No loss.
conftest.py copies lib_py/ to test sandboxes shutil.copytree(src_skills, ...) copies entire .agents/skills/ tree including new lib_py/agents/ subdirectory
deploy/remove.sh includes new files .agents/skills/lib_py directory entry covers agents/ subdirectory automatically
derive_session_name logic matches shell version f"{slug}-{role.lower()}-{name}" — identical
matches_session_name logic matches shell kind detection Checks -{role}-{name} and -{name} suffixes — consistent
own_key naming convention claude → session_id_own, others → conversation_id_own — matches existing schema
F-1 fix from prior review (job 11a99829) correctly applied `return 1 2>/dev/null

8. Verdict

The A-4 BaseAgentAdapter introduction and agent knowledge abstraction changes are well-designed, correctly implemented, and fully tested:

  • No regressions: All 74 tests pass (including 3 new contract tests)
  • No code loss: All existing consumers of load_state_json and verify_session_uuid continue to work; new Python module coexists with shell versions
  • Clean design: BaseAgentAdapter + static registry + per-agent adapters follows OOP best practices; resolve_home() contract prevents root-relative silent fail-close
  • Proper decoupling: reconcile.sh eliminates env-var code injection (exec(os.environ['MAM_VERIFY_PY'])) in favor of direct imports
  • CI coverage: Recursive glob fix ensures new nested Python files are compiled in CI

The 4 findings (F-1 through F-4) are all Low severity — unused imports, dead variable, consistency gap, and test fragility. None block the PASS verdict.

[VERDICT: PASS]