QA Engineer Skills 2026QA-2026Mapping ReAct to Vibe-Check Commands

Mapping ReAct to Vibe-Check Commands

Bridging Theory and Practice

The ReAct pattern is abstract: Observe, Think, Act, Evaluate. To make it concrete, we need to map each phase to actual commands that an AI agent can execute. The vibe-check CLI provides exactly these commands, making it a practical implementation of the ReAct loop for browser testing.


The Phase-to-Command Mapping

ReAct Phase Testing Activity Vibe-Check Commands
Observe Read the current page state vibe-check text "body", vibe-check url, vibe-check screenshot, vibe-check find-all "selector"
Think Decide what to test based on observation Agent reasons internally (no command -- LLM inference)
Act Execute the test action vibe-check type "selector" "text", vibe-check click "selector", vibe-check navigate "url"
Evaluate Check the result against expectations vibe-check text ".error", vibe-check url, vibe-check find "selector"

Observation Commands in Detail

The observation phase is about gathering state. Different commands reveal different aspects:

# Get the full visible text of the page (for content analysis)
vibe-check text "body"

# Get text from a specific element (for targeted checks)
vibe-check text ".notification-banner"

# Get the current URL (for navigation verification)
vibe-check url

# Take a screenshot (for visual state capture)
vibe-check screenshot -o current-state.png

# Find all elements matching a selector (for structural analysis)
vibe-check find-all "button"

# Get HTML of a section (for deep structural analysis)
vibe-check html

# Evaluate JavaScript (for dynamic state)
vibe-check eval "document.querySelectorAll('.error').length"

Action Commands in Detail

# Navigation
vibe-check navigate "https://app.example.com/login"

# Form interaction
vibe-check type "input[name=email]" "user@test.com"
vibe-check type "input[name=password]" "secret123"
vibe-check select "#country" "United States"

# Clicking
vibe-check click "button[type=submit]"
vibe-check click "#menu-toggle"

# Keyboard
vibe-check keys "Enter"
vibe-check keys "Ctrl+a"

# Scrolling
vibe-check scroll down
vibe-check scroll "footer"

# Hovering
vibe-check hover ".dropdown-trigger"

# Waiting
vibe-check wait "#loading-spinner" --state=hidden
vibe-check wait ".content" --state=visible

A Complete ReAct Test Using Vibe-Check

Here is how an agent thinks through a login test using the ReAct loop and vibe-check commands:

TEST OBJECTIVE: Verify that submitting the login form with empty fields
shows appropriate validation errors.

STEP 1 — OBSERVE:
  $ vibe-check navigate "https://app.example.com/login"
  $ vibe-check text "body"
  → "Login to Your Account | Email | Password | Sign In | Forgot Password?"

STEP 2 — THINK:
  "I see a login form with email and password fields and a Sign In button.
   My objective is to test empty field submission. I should click Sign In
   without filling in any fields."

STEP 3 — ACT:
  $ vibe-check click "button:has-text('Sign In')"

STEP 4 — OBSERVE:
  $ vibe-check text ".error-message"
  → "Email is required"
  $ vibe-check find-all ".field-error"
  → [
       { "selector": "#email-error", "text": "Email is required" },
       { "selector": "#password-error", "text": "Password is required" }
     ]

STEP 5 — THINK:
  "Two validation errors appeared: one for email and one for password.
   Both say the field is required. This matches expected behavior.
   Let me also verify we stayed on the login page (no navigation)."

STEP 6 — OBSERVE:
  $ vibe-check url
  → "https://app.example.com/login"

STEP 7 — THINK:
  "URL has not changed. We are still on the login page. Both required
   field errors are shown. Test objective is met."

STEP 8 — EVALUATE:
  DONE pass "Empty form submission correctly shows validation errors
  for both email ('Email is required') and password ('Password is required').
  Page did not navigate away from /login."

Implementing the Agent in Python

import subprocess
import json

class VibeCheckReActAgent:
    """A ReAct test agent using vibe-check CLI commands."""

    def __init__(self, llm, max_steps=15):
        self.llm = llm
        self.max_steps = max_steps
        self.history = []

    def vibe(self, command: str) -> str:
        """Execute a vibe-check command and return output."""
        result = subprocess.run(
            f"vibe-check {command}",
            shell=True, capture_output=True, text=True, timeout=30
        )
        if result.returncode != 0:
            return f"ERROR: {result.stderr.strip()}"
        return result.stdout.strip()

    def observe(self) -> dict:
        """Gather current page state using vibe-check commands."""
        return {
            "url": self.vibe("url"),
            "text": self.vibe('text "body"')[:2000],
            "title": self.vibe("title"),
            "errors": self.vibe('eval "document.querySelectorAll(\'.error\').length"'),
        }

    def run(self, objective: str) -> dict:
        """Execute a test using the ReAct loop."""
        for step in range(self.max_steps):
            # OBSERVE
            state = self.observe()

            # THINK + decide
            prompt = f"""
            You are a QA test agent. Your objective: {objective}

            Current state:
            - URL: {state['url']}
            - Page text (first 2000 chars): {state['text']}
            - Error count: {state['errors']}

            History (last 5 actions):
            {json.dumps(self.history[-5:], indent=2)}

            Respond with exactly ONE action:
            VIBE <command>  — execute a vibe-check command
            PASS <reason>   — test passed, explain why
            FAIL <reason>   — test failed, explain why
            """

            response = self.llm.generate(prompt).strip()
            self.history.append({"step": step, "state_url": state["url"], "action": response})

            # ACT or EVALUATE
            if response.startswith("VIBE "):
                command = response[5:]
                output = self.vibe(command)
                self.history[-1]["output"] = output
            elif response.startswith("PASS "):
                return {"status": "pass", "reason": response[5:], "steps": step + 1}
            elif response.startswith("FAIL "):
                return {"status": "fail", "reason": response[5:], "steps": step + 1}

        return {"status": "timeout", "reason": f"Exceeded {self.max_steps} steps", "steps": self.max_steps}

Error Recovery Patterns

One of the most powerful aspects of mapping ReAct to vibe-check is the agent's ability to recover from errors:

Selector Not Found Recovery

Agent tries: vibe-check click "#submit-btn"
Output: ERROR: No element found for selector '#submit-btn'

Agent observes: vibe-check find-all "button"
Output: [
  {"selector": "button.primary-action", "text": "Submit"},
  {"selector": "button.cancel", "text": "Cancel"}
]

Agent reasons: "The submit button exists but has a different selector.
              I should use 'button.primary-action' instead."

Agent acts: vibe-check click "button.primary-action"
Output: success

Unexpected State Recovery

Agent tries: vibe-check click "#checkout-btn"
Output: success

Agent observes: vibe-check text "body"
Output: "Your session has expired. Please log in again."

Agent reasons: "Session expired during checkout. This is an environment
              issue. I need to log in again before continuing the test."

Agent acts:
  vibe-check type "#email" "test@example.com"
  vibe-check type "#password" "password123"
  vibe-check click "#login-btn"
  // Then retries the checkout

Performance Considerations

Metric Vibe-Check Agent Direct Playwright Script
Steps per test 8-20 Fixed (known at write time)
Time per step (LLM) 200-500ms 0ms (no reasoning)
Time per step (browser) 100-300ms 100-300ms
Total test time 5-15 seconds 1-3 seconds
Adaptability High None
Debugging difficulty Medium (trace history) Low (linear script)

Key Takeaway

The vibe-check CLI provides a natural mapping from the abstract ReAct pattern to concrete browser automation commands. Each ReAct phase has corresponding vibe-check commands: observation via text/url/find-all/screenshot, action via click/type/navigate, and evaluation via assertion-like text and url checks. This mapping turns the theoretical ReAct loop into a practical, implementable test agent.