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 |
ClaudeAgentAdapter — own_key = claude_session_id_own |
lib_py/agents/adapters/agy.py |
AgyAgentAdapter — own_key = agy_conversation_id_own |
lib_py/agents/adapters/hermes.py |
HermesAgentAdapter — own_key = hermes_conversation_id_own |
lib_py/agents/adapters/cline.py |
ClineAgentAdapter — own_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.py → paths, verify_session; registry.py → base + 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:
resolve_homecontract: explicit arg, HOME_DIR env, HOME/expanduser fallback- Adapter registry: all 4 agents have correct
nameandown_key(claude →session, others →conversation) agent_of_rowpriority: explicitagentfield > 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_DIRenv →HOMEenv →expanduser('~') - Fail-close: raises
ValueErrorif result is empty or/— prevents root-relative silent fail-close - Behavioral change in
verify_session.py: Old codehome_dir or os.environ.get("HOME_DIR", "")silently returned""→ now raisesValueError. 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 codeos.environ['HOME_DIR']raisedKeyErroron missing env → now falls back toHOME/expanduser. More robust.
5.2 BaseAgentAdapter & Registry (N4 & N5)
Clean OOP design:
BaseAgentAdapterdefinesname,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 inlib.shderive_session_name()returnsf"{slug}-{role}-{name}"— matches the shellderive_session_namefunctionagent_of_row()implements strict priority: explicitagentfield → session name suffix →pane.cmdexact/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_surrogatesrecursive cleaner - Identical
sqlite3.connect(timeout=60.0)+PRAGMA busy_timeout = 60000 - Enhancement: accepts
yaml_pathparameter with env var fallback (AGENT_SESSIONS_YAMLorYAML_PATH) — more flexible than shell version which always getsYAML_PATHfromenv_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 patternMAM_VERIFY_PY="$VERIFY_SESSION_PYTHON"removed from both dry-run and write paths — no more env-var couplingload_state_jsonnow 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 bodybase.py:4:from typing import Optional, Dict, Any, List—Listunused__main__.py:3:import sys, json—jsonunused- 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:1121definesVERIFY_SESSION_PYTHON="$(cat ...)"but no consumer remains —reconcile.shwas the only consumer (viaMAM_VERIFY_PY), now removed.- The
if-elseblock 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 toresolve_home() reconcile.shalways setsHOME_DIR="$HOME_DIR"via shell wrapper (line 71), soKeyErroris impossible in practice- Impact: Inconsistency with the
resolve_home()pattern adopted inverify_session.pyandworkspace_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_copywithHOME_DIRpopped, computes expectedhome_valfrom the copy, but callsresolve_home()with the actualos.environ(which may still haveHOME_DIRset from prior tests or environment). - If
HOME_DIRwas set in the environment when the test runs,resolve_home()would return theHOME_DIRvalue, buthome_valwould beHOME/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_jsonandverify_session_uuidcontinue 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.sheliminates 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]