Consolidation: extract a shared subprocess-lifecycle base for LLC adapters
PR #9811 (#9789/#9769) introduced llc/adapters/subprocess_support.py to share the prompt + credential layer across the 4 subprocess adapters. But the subprocess lifecycle is still duplicated near-verbatim between the two base adapters (claude_code_adapter.py, 354 lines; copilot_local_adapter.py, 277 lines):
_output_path, _state_path, _load_state (path-traversal-guarded JSON read)
_probe_pid (os.kill(0) → RUNNING/COMPLETED/FAILED)
_resolve_timeout (3-tier timeout hierarchy)
status / _status (state load → timeout check → probe)
_cancel (SIGTERM → grace loop → SIGKILL → unlink state) — byte-identical except log strings + claude's extra _clear_session
- constants:
_SIGTERM_GRACE_SECONDS, _ADAPTER_TIMEOUT_SECONDS, _DEFAULT_OUTPUT_DIR
copilot_local_adapter's own docstring says it "mirrors ClaudeCodeAdapter" — the mirroring is the smell. ~150 lines of near-identical code that can drift (e.g. the #9763-class test bug, or timeout logic changing in one but not the other).
Proposed fix
Extract a SubprocessLifecycleAdapter base class (in subprocess_support.py or a new subprocess_base.py) owning state-file management, PID probe, timeout resolution, status, and cancel — parameterised by:
- a CLI resolver (
_resolve_claude_cli / _resolve_gh_cli)
- output/state filename prefix (
llc_agent_ vs llc_copilot_)
- an optional post-cancel hook (claude's
_clear_session)
ClaudeCodeAdapter / CopilotLocalAdapter then only implement invoke (command assembly + their specific env). The subscription subclasses keep inheriting.
References
Discovered auditing the merged adapter-consolidation work (PR #9811). Builds on subprocess_support.py. Related: #9008, #9033.
Labels: enhancement · backend · agents · priority: low
Consolidation: extract a shared subprocess-lifecycle base for LLC adapters
PR #9811 (#9789/#9769) introduced
llc/adapters/subprocess_support.pyto share the prompt + credential layer across the 4 subprocess adapters. But the subprocess lifecycle is still duplicated near-verbatim between the two base adapters (claude_code_adapter.py, 354 lines;copilot_local_adapter.py, 277 lines):_output_path,_state_path,_load_state(path-traversal-guarded JSON read)_probe_pid(os.kill(0) → RUNNING/COMPLETED/FAILED)_resolve_timeout(3-tier timeout hierarchy)status/_status(state load → timeout check → probe)_cancel(SIGTERM → grace loop → SIGKILL → unlink state) — byte-identical except log strings + claude's extra_clear_session_SIGTERM_GRACE_SECONDS,_ADAPTER_TIMEOUT_SECONDS,_DEFAULT_OUTPUT_DIRcopilot_local_adapter's own docstring says it "mirrors ClaudeCodeAdapter" — the mirroring is the smell. ~150 lines of near-identical code that can drift (e.g. the #9763-class test bug, or timeout logic changing in one but not the other).Proposed fix
Extract a
SubprocessLifecycleAdapterbase class (insubprocess_support.pyor a newsubprocess_base.py) owning state-file management, PID probe, timeout resolution,status, andcancel— parameterised by:_resolve_claude_cli/_resolve_gh_cli)llc_agent_vsllc_copilot_)_clear_session)ClaudeCodeAdapter/CopilotLocalAdapterthen only implementinvoke(command assembly + their specific env). The subscription subclasses keep inheriting.References
Discovered auditing the merged adapter-consolidation work (PR #9811). Builds on
subprocess_support.py. Related: #9008, #9033.Labels: enhancement · backend · agents · priority: low