Skip to content

feat(qa): CLI E2E harness tool for terminal applications#415

Merged
OBenner merged 3 commits into
developfrom
feat/cli-e2e-harness
Jul 12, 2026
Merged

feat(qa): CLI E2E harness tool for terminal applications#415
OBenner merged 3 commits into
developfrom
feat/cli-e2e-harness

Conversation

@OBenner

@OBenner OBenner commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Final roadmap phase (5): verification beyond web/Electron, starting with the cheapest high-coverage slice — terminal applications (most C/C++/Go/Rust deliverables are CLIs).

QA agents can already drive Electron apps (Electron MCP) and web apps (Puppeteer), but had no way to run the thing they just built when it's a CLI. New run_cli_session auto-claude MCP tool:

  • Pseudo-terminal on POSIXisatty(), prompts, and colored output behave as for a real user; plain-pipe fallback on Windows (no stdlib pty)
  • Scripted stdin — send input lines in order with prompt-friendly pacing
  • Expectations — reports found/missing substrings with PASS/FAIL, exit code, timeout flag, and output tail
  • Security — the command passes through the same validate_command allowlist as Bash, always runs inside the project directory, output capped at 100KB, timeout clamped to 300s
  • Wired into qa_reviewer and qa_fixer tool permissions only (not coder)

Split: core/cli_session.py (pure runner, testable) + agents/tools_pkg/tools/cli_harness.py (MCP wrapper + security gate).

Testing

tests/test_cli_session.py: 10 tests — output/exit-code capture, stderr, scripted stdin against a real prompting script, timeout kill, output cap, cwd, empty command, and QA-only registration. tests/test_agent_configs.py + test_agent_architecture.py: 46 passed. Live registration check: create_all_tools() exposes run_cli_session. Ruff check + format clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added end-to-end CLI session verification for interactive terminal applications.
    • CLI sessions support scripted input, output capture, timeout handling, exit-status reporting, and expectation checks.
    • QA verification workflows can now run and validate CLI commands securely.
  • Bug Fixes

    • Prevented runaway CLI output and ensured hung sessions are terminated after the configured timeout.
  • Tests

    • Added coverage for successful and failed commands, input handling, stderr capture, timeouts, output limits, and tool availability.

QA agents can E2E-test Electron/web apps (Electron MCP, Puppeteer) but
had no way to drive terminal applications. New run_cli_session MCP tool:

- Spawns the command in a pseudo-terminal on POSIX (prompts, colors,
  isatty() behave like a real terminal); plain pipes on Windows
- Feeds scripted stdin lines, captures combined output (capped),
  enforces a timeout, reports found/missing expected substrings
- Commands validated against the project security allowlist and always
  executed inside the project directory
- Available to qa_reviewer and qa_fixer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@OBenner, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 95e70029-8cba-459f-b5cf-db0c3c6e2256

📥 Commits

Reviewing files that changed from the base of the PR and between 158fc43 and b76e8de.

📒 Files selected for processing (2)
  • apps/backend/core/cli_session.py
  • tests/test_cli_session.py
📝 Walkthrough

Walkthrough

Changes

CLI session harness

Layer / File(s) Summary
CLI session runner
apps/backend/core/cli_session.py, tests/test_cli_session.py
Adds cross-platform CLI execution with PTY or pipes, scripted input, timeout handling, output truncation, and structured results, with coverage for these behaviors.
MCP CLI harness tool
apps/backend/agents/tools_pkg/tools/cli_harness.py
Adds command validation, CLI execution, expectation matching, and formatted MCP text results.
QA tool wiring and coverage
apps/backend/agents/tools_pkg/tools/__init__.py, apps/backend/agents/tools_pkg/registry.py, apps/backend/agents/tools_pkg/models.py, tests/test_cli_session.py
Exports and registers the harness tool and enables it for QA reviewer and fixer configurations while excluding coder configurations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant QAAgent
  participant run_cli_session
  participant SecurityAllowlist
  participant CLIApplication
  QAAgent->>run_cli_session: submit command, inputs, expectations
  run_cli_session->>SecurityAllowlist: validate command
  run_cli_session->>CLIApplication: run session in project directory
  CLIApplication-->>run_cli_session: return exit code and captured output
  run_cli_session-->>QAAgent: return verification report
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a QA CLI E2E harness tool for terminal applications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-e2e-harness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

shlex.split(posix=False) keeps surrounding quotes, so quoted arguments
reached Windows processes with literal quote characters. CreateProcess
parses the command line itself; pass the string through unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/backend/core/cli_session.py`:
- Around line 157-161: Update the finally cleanup block in the CLI session flow
so failure closing master_fd cannot prevent process cleanup. Make
os.close(master_fd), the proc.kill() check, and proc.wait() execute
independently, ensuring proc is terminated and reaped even when closing the file
descriptor raises OSError.
- Around line 146-148: Update the write handling around the event loop’s
pending-input logic to check whether master_fd is writable before calling
os.write, using the existing select/poll mechanism or a nonblocking descriptor.
Only pop the pending item and advance next_write after a successful write, while
allowing the loop’s timeout and deadline checks to continue when the pty buffer
is full.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 029004a7-3dd5-449e-814b-df33880d7f43

📥 Commits

Reviewing files that changed from the base of the PR and between 4aa2449 and 158fc43.

📒 Files selected for processing (6)
  • apps/backend/agents/tools_pkg/models.py
  • apps/backend/agents/tools_pkg/registry.py
  • apps/backend/agents/tools_pkg/tools/__init__.py
  • apps/backend/agents/tools_pkg/tools/cli_harness.py
  • apps/backend/core/cli_session.py
  • tests/test_cli_session.py

Comment thread apps/backend/core/cli_session.py Outdated
Comment thread apps/backend/core/cli_session.py
Address review comments:
- os.write on a full pty buffer could block forever when the child
  never reads stdin, bypassing the timeout. Master fd is now
  non-blocking with a write buffer (_flush_input handles partial
  writes and EAGAIN); BlockingIOError on read treated as no-data,
  not EOF.
- finally block steps are now independent: a failing os.close can no
  longer skip proc.kill()/wait() and leak a zombie process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@github-actions github-actions Bot added size/L and removed size/M labels Jul 12, 2026
@sonarqubecloud

Copy link
Copy Markdown

# Independent cleanup steps: a failure in one must not skip the rest
try:
os.close(master_fd)
except OSError:
@OBenner OBenner merged commit d055374 into develop Jul 12, 2026
19 checks passed
OBenner added a commit that referenced this pull request Jul 12, 2026
All five phases landed: #387, #388 (phase 1), #390, #394 (phase 2),
#395 (phase 3), #402, #412 (phase 4), #415 (phase 5, CLI slice).

Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants