# Cross-Code Review Report: B-8 send_keys_safe agy Path Bypass Removal **Job ID**: d629619a **Reviewer**: cline (session: herdr:canary-projects-multi-agent-mux-creator-cline) **Review Target**: B-8 `send_keys_safe` agy unconditional `return 0` bypass removal and verification integrity cross-check **Base Commit**: f8bfa07 (clean tree except working changes) **Date**: 2026-08-13 --- ## 1. Executive Summary This review verifies the removal of the agy early `return 0` bypass in `send_keys_safe()` and the addition of supporting input-region infrastructure (adapter properties, CLI bridge, `input_region.py` module). The change set includes 5 modified files and 2 new files. **Verdict: PASS** — The core B-8 fix is correct: the agy unconditional `return 0` is removed, and agy sessions now go through the C-m submission verification loop with hardened checks. The test confirms returncode 4 on submission failure (not 0). However, the supporting `input_region.py` module has a missing `base.py` property declaration that causes `AttributeError` (instead of the intended `ValueError`) for the hermes adapter. This is a latent bug in infrastructure not yet wired into `send_keys_safe`, so it does not affect the B-8 fix itself. --- ## 2. Change Set | File | Change | Lines | |---|---|---| | `.agents/skills/lib.sh` | Remove agy early return 0; add agy to skip-paste-check group | -6/+2 | | `.agents/skills/lib_py/agents/__main__.py` | Add `input-region` CLI command + emit INPUT_* facts | +15 | | `.agents/skills/lib_py/agents/adapters/agy.py` | Add input_prompt/placeholder/rule_pattern | +12 | | `.agents/skills/lib_py/agents/adapters/claude.py` | Add input_prompt/placeholder/rule_pattern | +12 | | `.agents/skills/lib_py/agents/adapters/cline.py` | Add input_prompt/placeholder/rule_pattern | +12 | | `.agents/skills/lib_py/agents/input_region.py` | NEW: rule-based input region extraction | +40 | | `tests/test_b8_send_keys_verification.py` | NEW: bash mock test for agy submission failure | +29 | --- ## 3. Core B-8 Fix Analysis (lib.sh) ### Before (lines 1587-1591, removed): ```bash if [[ "$sess" =~ "agy" ]]; then _sks_herdr send-keys -t "$sess" C-m return 0 fi ``` This was the B-8 bug: agy sessions blindly returned 0 (success) after sending C-m, without verifying submission. ### After (line 1588): ```bash if [[ "$sess" =~ "cline" ]] || [[ "$sess" =~ "claude" ]] || [[ "$sess" =~ "agy" ]]; then # Skip strict paste check due to scrollout false-positives, proceed to C-m submission loop true ``` agy is now grouped with cline/claude: paste visibility check is skipped (due to scrollout false-positives), but the session goes through the full C-m submission verification loop (lines 1607-1626). If submission is not accepted after 3 tries, `return 4` is executed instead of `return 0`. **Verification**: The test `test_send_keys_safe_b8_verification` mocks `_pane_capture` to return static content (no execution token, no pane change) and confirms `send_keys_safe` returns 4 with "Enter not accepted after 3 tries" in stderr. ✅ --- ## 4. Supporting Infrastructure Analysis ### 4.1 Adapter Properties (agy.py, claude.py, cline.py) Each adapter now defines `input_prompt`, `input_placeholder`, and `input_rule_pattern`: | Adapter | input_prompt | input_placeholder | input_rule_pattern | |---|---|---|---| | agy | `>` | `` | `─{10,}` | | claude | `❯` | `` | `─{10,}` | | cline | `❯` | `Ask anything...` | `─{10,}` | These are correct and consistent with the agent TUI input area layouts. ✅ ### 4.2 CLI Bridge (__main__.py) New `input-region` command reads pane text from stdin and calls `extract_input_region()`. On success, prints region and exits 0. On exception, prints error to stderr and exits 5. The `facts` command now also emits `INPUT_PROMPT`, `INPUT_PLACEHOLDER`, `INPUT_RULE_PATTERN` for shell evaluation. ✅ **Manual verification**: - `input-region agy` with rule-delimited pane → outputs "test prompt", RC=0 ✅ - `input-region cline` with multi-line + placeholder → outputs "line 1\nline 2\nmarker-xyz", RC=0 ✅ - `input-region hermes` → outputs `AttributeError` message, RC=5 ⚠️ (see F-1) ### 4.3 input_region.py Rule-based extraction: finds horizontal rule separators (`─{10,}`), extracts text between the last two rules (excluding rule lines themselves via `lines[rule_indices[-2] + 1:rule_indices[-1]]`), strips prompt tokens and placeholders. Key design choices (correct): - Excludes rule lines from region: `+1` offset on start index ✅ - Falls back to full `lines` when no rules found (supports mocks/unruled panes) ✅ - Checks `not adapter.input_prompt` for unsupported agent detection ✅ - Uses `adapter.input_rule_pattern or r'─{10,}'` fallback ✅ ### 4.4 Test (test_b8_send_keys_verification.py) Bash mock test that sources `lib.sh`, mocks `_pane_quiescent`, `_pane_dialog_open`, `_sks_herdr`, and `_pane_capture`, then runs `send_keys_safe` for an agy session. Expects returncode 4 and "Enter not accepted after 3 tries" in stderr. Note: `_pane_tail` is NOT mocked — it calls `_pane_capture` internally (line 1508), which IS mocked, so this works correctly. ✅ --- ## 5. Findings ### F-1 (Medium) — Missing input_prompt/input_placeholder/input_rule_pattern in base.py **Location**: `.agents/skills/lib_py/agents/base.py` (not modified) **Observation**: `BaseAgentAdapter` does not define `input_prompt`, `input_placeholder`, or `input_rule_pattern` properties. Only agy, claude, and cline adapters override them. `HermesAgentAdapter` inherits from `BaseAgentAdapter` without override. **Impact**: When `input_region.py` line 14 accesses `adapter.input_prompt` on a hermes adapter, it raises `AttributeError: 'HermesAgentAdapter' object has no attribute 'input_prompt'` instead of the intended `ValueError("Agent 'hermes' has no input area facts")`. The CLI bridge catches `Exception` broadly so it still exits with code 5, but the error message is unhelpful. **Verified**: `hasattr(get_adapter('hermes'), 'input_prompt')` returns `False`. **Fix**: Add to `base.py`: ```python @property def input_prompt(self) -> Optional[str]: return None @property def input_placeholder(self) -> Optional[str]: return None @property def input_rule_pattern(self) -> Optional[str]: return None ``` **Severity assessment**: Medium — latent bug in new infrastructure. Does not affect the B-8 fix itself (d629619a's lib.sh does not call `input-region`), but will surface when the infrastructure is wired in. ### F-2 (Low) — Usage string in __main__.py doesn't mention input-region **Location**: `.agents/skills/lib_py/agents/__main__.py:8` **Observation**: Usage string says `Usage: python -m lib_py.agents [args...]` but `input-region` is now a supported command. **Fix**: Update to `Usage: python -m lib_py.agents [args...]` ### F-3 (Info) — input_region.py not wired into send_keys_safe **Observation**: The `input_region.py` module and CLI bridge are added but not called from `send_keys_safe()`. The lib.sh change is purely the agy early-return removal. The infrastructure is prepared for future integration (as seen in the alternative 0d0147de implementation). **Assessment**: Acceptable incremental approach. The B-8 fix is self-contained. ### F-4 (Info) — [A-Za-z]+ing fast-path regex retained **Location**: `.agents/skills/lib.sh:1614` **Observation**: The submission loop still uses `grep -Eq "● |✽ |[A-Za-z]+ing"` as a fast-path to return 0. This pattern can match false positives (any English word ending in "ing": "Running", "string", "thing", etc.). **Assessment**: Pre-existing issue, NOT introduced by this change. The 0d0147de alternative removes this fast-path. Non-blocking for d629619a. --- ## 6. Test Results | Test | Result | |---|---| | `test_b8_send_keys_verification.py::test_send_keys_safe_b8_verification` | PASS | | `test_a4_adapter_contract.py::test_resolve_home_contract` | PASS | | `test_a4_adapter_contract.py::test_agent_adapter_registry` | PASS | | `test_a4_adapter_contract.py::test_agent_of_row_priority` | PASS | ### Static Analysis | Check | Result | |---|---| | `bash -n .agents/skills/lib.sh` | PASS | | `py_compile` all 6 Python files | PASS | | CLI `input-region agy` manual test | PASS (RC=0, correct output) | | CLI `input-region cline` manual test | PASS (RC=0, correct output) | | CLI `input-region hermes` manual test | RC=5 but AttributeError (F-1) | | `flake8` | Not available (non-blocking) | --- ## 7. Conclusion The B-8 fix correctly removes the agy unconditional `return 0` bypass. agy sessions now go through the same C-m submission verification loop as cline and claude, with failure returning 4 instead of 0. The test validates this behavior. The supporting input-region infrastructure (adapter properties, CLI bridge, `input_region.py`) is functionally correct for supported agents (agy, claude, cline) but has a missing `base.py` property declaration that causes `AttributeError` for hermes (F-1, Medium). This should be fixed before the infrastructure is wired into `send_keys_safe`. No design-level rework or escalation is needed. [VERDICT: PASS]