fix(lib): resolve B-8 send_keys_safe agy verification bypass and add submission retry test
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# 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 <facts|resolve> [args...]` but `input-region` is now a supported command.
|
||||
**Fix**: Update to `Usage: python -m lib_py.agents <facts|resolve|input-region> [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]
|
||||
Reference in New Issue
Block a user