The Principle

AI should assist, not decide. The tester drives intent, controls meaning, and owns judgment. The tool captures actions, transcribes voice, and structures findings. It never decides what is a finding. That's your job.

ettool is a session recorder for exploratory testing. You launch a browser, explore the application, and talk through what you see. Press a hotkey when something is worth noting. When you're done, the tool correlates your voice with your browser actions and generates structured findings in org-mode format, ready for review in Emacs and posting to GitHub PRs.

Most "AI testing tools" try to find bugs for you. ettool does the opposite: it assumes you're the expert and handles the paperwork. The LLM structures what you described. It never invents expected behavior, never upgrades an observation to a bug, and never infers what you meant. If you didn't say it, it doesn't appear in the report.

3,500+ Lines of Python
178 Unit Tests
5 Finding Types
14 Modules

How It Works

1. Record

During the session · Hands-free capture

Launch a browser with ettool start --pr 123 --charter "Test login flow". Three systems run concurrently: a browser controller captures clicks, navigation, and input. An audio recorder captures your microphone. A hotkey handler listens for your commands.

You test normally. When something catches your attention, press Ctrl+Shift+F7 and classify it. The tool takes a screenshot and marks the moment. When you're done, Ctrl+Shift+F8 ends the session.

2. Transcribe

Post-session · faster-whisper with GPU acceleration

The recorded audio goes through faster-whisper with VAD filtering and word-level timestamps. Full-buffer transcription, not streaming. This matters: full context produces significantly better transcription quality than real-time streaming, which I verified empirically by building both approaches and comparing the output.

3. Correlate

The core pipeline · Three-pass processing

The correlation pipeline merges browser actions, transcript segments, and captures into a unified timeline sorted by timestamp. For each finding, it gathers context from a 60-second window: what you said, what you clicked, what pages you visited.

An LLM then generates titles and structured descriptions from this context. Different finding types get different prompts. Bugs get steps to reproduce (only if evident from the narration). Expected behavior is included only if you explicitly stated it.

4. Review

Emacs · Custom review mode with GitHub integration

Findings open in Emacs as an org-mode file with a custom major mode. Navigate between findings, view inline screenshots, edit descriptions, and post directly to GitHub PRs. Screenshots are uploaded to a dedicated branch and embedded in PR comments. Nothing leaves your machine until you explicitly approve it.

The Hotkey System

During a session, you classify findings yourself. The tester decides what's a bug and what's just an observation. The tool never guesses.

Hotkey Action
Ctrl+Shift+F5 Take screenshot
Ctrl+Shift+F6 Create checkpoint (screenshot + DOM snapshot + console + network state)
Ctrl+Shift+F7 Mark finding (opens type selector)
Ctrl+Shift+F8 End session

When you press Ctrl+Shift+F7, an in-browser dialog appears with five finding types:

Bug Issue Comment Observation Question

Each type carries different certainty and tone. A bug is confirmed and definitive. An issue is uncertain and invites discussion. An observation is neutral, noted for the record. The classification drives how the LLM structures the output.

What You Get

The output is an org-mode file with structured findings, context quotes from your narration, embedded screenshots, and a raw timeline of the session.

** TODO Cohort filter reloads page on each selection :bug: :PROPERTIES: :FINDING_ID: F001 :TIMESTAMP: 02:09 :URL: https://app.example.com/health-summary/?cohort=919 :SCREENSHOT_1: ss-109732.png :END: **[Description]:** I found that when I select a cohort from the filter dropdown, the entire page reloads instead of updating the view in place. **[Steps to reproduce]:** 1. I navigated to the Health Summary page 2. I clicked the cohort dropdown 3. I selected any cohort option **[Expected behavior]:** I expected the page to filter results without a full reload. *** Context #+BEGIN_QUOTE [02:03] Said: "The bug is basically that as soon as I select one cohort..." [02:18] Said: "As soon as I click on one, the page reloads..." #+END_QUOTE

Findings are written in first person because you'll post them as your own observations. Steps to reproduce only appear for bugs where clear steps are evident. Expected behavior only appears if you said what you expected. The context section preserves your exact words so you can verify the LLM didn't embellish.

What I Built and Then Rejected

Real-Time Voice Detection

I built a complete real-time voice pipeline: streaming transcription with GPU-accelerated Whisper, LLM intent detection every 1.5 seconds, automatic screenshot capture when findings were detected. It worked. I rejected it after testing because the AI was deciding what counted as a finding instead of the tester. Classification quality was poor, and the loss of control felt wrong. Hotkeys give the tester explicit control. Voice detection guesses. The branch is preserved as an empirical record of why the simpler approach wins.

I also evaluated seven local LLM models for the correlation pipeline. All of them conflated distinct issues into single findings. Only Claude Sonnet correctly separated them. At roughly two cents per session, the API cost isn't worth optimizing away at the expense of finding quality and trust in the tool.

Technology

Python 3.11+ with Playwright for browser automation, faster-whisper for transcription (CTranslate2, GPU-accelerated), pynput for global hotkeys, Claude Sonnet API for correlation, and a custom Emacs major mode for review with GitHub PR integration via the gh CLI.

Configuration is layered TOML (project-local overrides global overrides defaults). Browser profiles persist cookies across sessions so you log in once. Output is plain-text org-mode with no vendor lock-in.

Status

ettool is in active development and daily use for real testing work. The full pipeline works end-to-end. Open source release is planned but not yet scheduled.

The tool and its development story (including the real-time voice rejection) are part of my conference talks on AI accountability and the difference between fast implementation and active architecture.

Interested?

If you're working on similar problems, whether building tools for exploratory testing, thinking about where AI belongs in the testing workflow, or designing human-governed AI systems, I'd enjoy the conversation.