ReeQA

ReeQA is a separate Reepolee app that runs on its own process and port (REEQA_PORT, default 2340). It provides a QA dashboard for running two kinds of checks against any project in your checkout: command checks (run a shell command like bun test and capture pass/fail) and visual baselines (capture headless-browser screenshots of pages, then compare later runs against the baseline to detect visual drift).

It shares Reepolee's renderer, auth, route pipeline, translations, components, and static files - so the dashboard itself is built with the same tools as the app it tests. QA commands and browser captures run as isolated child processes and cannot block the main app server.

Starting ReeQA

ReeQA runs on its own port, independent of the main app server:

bun dev:reeqa          # ReeQA only (port 2340)
bun dev:all             # app + reeman + reeqa + queue worker together

In production, ReeQA runs as its own process under systemd or PM2:

bun start:reeqa         # bun apps/reeqa/server.ts --prod

The dashboard is at http://localhost:2340. It uses the same login and users table as the main app - you log in with the same credentials. See Quick Start for the full dev-process table.

The Dashboard

The dashboard (/) shows an overview of the active project: pass/fail counts for recent command-check runs, the active page set and its baseline status, and any currently running check or visual comparison. A /__busy endpoint reports whether a run is in progress, used by the shared layout's busy-poller.

The sidebar navigates between four main sections:

RoutePagePurpose
/DashboardOverview: recent runs, pass/fail counts, baseline status
/projectsProjectsRegister projects, create and manage page sets
/run-testsRun TestsCapture baselines, run visual comparisons, schedule recurring checks
/command-checksCommand ChecksRun QA suites (shell commands) and view their output

Projects

A project is a path on disk with a package.json and a base URL. ReeQA manages multiple projects, but one is active at a time - the dashboard and run pages operate against the active project.

To register a project, provide:

  • Name - a human-readable label
  • Path - an absolute path to a directory containing package.json
  • Base URL - the HTTP/HTTPS origin ReeQA will capture pages from

The path must be absolute and must contain a package.json - this is how ReeQA discovers available QA suites (it checks whether the project's package.json defines the required script).

Projects are stored in .reepolee/reeqa/projects.json. The active project is tracked in .reepolee/reeqa/active-project.json. Deleting a project also deletes its page sets and clears the active selection.

Command Checks

Command checks run a shell command in the project's directory and report pass (exit code 0) or fail (non-zero). They are the simplest QA primitive: pick a suite, run it, read the output.

QA Suites

ReeQA ships with four built-in suites, each backed by a shell command:

Suite codeNameCommandRequires script
testsWebsite testsbun test-
engine-driftEngine driftbun run engine:checkengine:check
namingNaming compliancebun run naming:checknaming:check
docs-linksDocumentation linksbun run docs:checkdocs:check

A suite is available for a project when its required_script exists in the project's package.json scripts (or when it has no required_script, like tests). The /command-checks page shows only available suites.

Running a Suite

Pick a suite from the dropdown and submit. ReeQA spawns the command as a child process in the project's directory with CI=1 and NO_COLOR=1 set (so output is plain text and tests run in CI mode). The run is:

  • Queued - the job goes to the queue worker (or runs in-process if the queue is unavailable)
  • Running - stdout and stderr are streamed into the run record (up to 2 MB, then the head is truncated)
  • Passed/Failed - exit code 0 is passed, anything else is failed
  • Canceled - if you cancel mid-run

Only one command check can run at a time. If a run is already in progress, starting another returns an error.

Run state is persisted to .reepolee/reeqa/runs.json with atomic writes (temp file + rename), so a server restart does not lose run history. A stale running or queued run (from a crash) is re-executed by the worker on next startup.

Page Sets

A page set defines which pages ReeQA captures and compares. Each page set belongs to a project. One page set per project is active at a time.

There are two kinds of page set:

URL-list Page Sets

A list of URLs from the project's sitemap.xml. ReeQA fetches the sitemap, presents the pages (sorted by lastmod, newest first), and you check which ones to include. Every URL must be in the current sitemap - this validation runs on create and update.

This is the simplest model: one screenshot per URL, compared pixel-for-pixel against the baseline.

Workflow Page Sets

A JSON array of steps that drive a headless Chrome browser through an interaction flow (login, fill a form, click a button) and capture screenshots at marked checkpoints. This is for pages that are only reachable after an action, or states that require setup (logged-in dashboard, post-submit confirmation, etc.).

Three step types:

Step typeFieldsDescription
navigateurl, checkpoint?, before_seconds?, delay_seconds?Open a URL. Must be the first step.
clickselector or text, checkpoint?, timingsClick an element by CSS selector or visible text
fillselector, value or value_env, checkpoint?, timingsFill an input field
[
    { "type": "navigate", "url": "https://example.com/login" },
    { "type": "fill", "selector": "#username", "value_env": "QA_USERNAME" },
    { "type": "fill", "selector": "#password", "value_env": "QA_PASSWORD" },
    { "type": "click", "text": "Sign in", "checkpoint": true }
]

Validation rules:

  • The first step must be navigate.
  • At least one step must have "checkpoint": true - a workflow with no checkpoint captures nothing to compare.
  • click must have exactly one of selector or text (not both, not neither).
  • fill must set value (a literal string) or value_env (an environment variable name resolved from the project's .env file) - not both. ReeQA never stores secrets: only the variable name is saved in the page set; the value is resolved at run time.
  • Unknown fields are rejected (a typo like chekpoint produces an error, not a silent no-op).
  • navigate targets must stay on the project's own origin (a login POST target legitimately is not in the sitemap, but it must be on the same domain).

before_seconds and delay_seconds pause before and after each step's action. outline_seconds and glide_seconds (click steps only) pace the recording's click presentation - they are harmless in a capture, since screenshots are taken after the step already ran.

Capture Size

Each page set has a capture size - the browser viewport used for both baseline capture and comparison. Three presets:

PresetWidthHeight
mobile390844
tablet7681024
desktop19201080

Capture and compare use the same size so pages render identically (including scrollbar gutters).

Auto-Evidence and Auto-Recording

Two optional flags on a page set:

  • auto_evidence - after a compare run finishes, automatically record a narrated evidence video for every changed/new page, instead of requiring a manual "Record evidence" click per page.
  • auto_recording - after a compare run finishes, also record a clean (un-annotated) video for unchanged (passing) pages - the only way a video exists for a test that passed.

Both use the queue worker; if the queue is unavailable, they run in-process as a fallback.

Page sets are stored in .reepolee/reeqa/page-sets.json. The active page set is tracked in .reepolee/reeqa/active-page-set.json.

Visual Baselines and Comparison

The /run-tests page is where you capture baselines and run comparisons. The workflow is:

  1. Select a project and page set (done on the /projects page).
  2. Capture a baseline - ReeQA opens a headless Chrome at the page set's capture size, navigates to each URL (or replays each workflow step), captures a full-page screenshot plus a DOM snapshot and HTML, and stores them as the baseline. If the project has a db:clone-test script, the test database is reset and snapshotted alongside the images, so a later compare restores the identical starting state.
  3. Run a comparison - ReeQA restores the DB snapshot (if one exists), re-captures every page, and compares each screenshot against the baseline by SHA-256 hash. If the hash differs, a pixel-level diff image is generated with vips, and the changed DOM elements are identified from the stored DOM snapshot.

Page Status

Each page in a comparison report gets one of:

StatusMeaning
unchangedScreenshot hash matches the baseline exactly
changedHash differs; a diff image and changed-element list are generated
newThe page exists in the current run but not in the baseline
removedThe page existed in the baseline but was not captured this run
baselineThe page was captured as part of a baseline run (no comparison)

Evidence Recording

For changed or new pages, you can record a narrated evidence video - Chrome replays the steps to reach the page, then captures a screencast with on-screen annotations (action labels, cursor movement, click ripples) and text-to-speech narration. The video is produced with ffmpeg. This runs as a queue job (reeqa_evidence) and the server is notified via a WebSocket relay when the video is ready.

For unchanged pages with auto_recording enabled, a clean (un-annotated) clip is recorded instead (reeqa_recording job).

Scheduling

You can schedule a recurring baseline capture or comparison for a project + page set at an interval of 1+ hours. The scheduler ticks every 60 seconds and starts any due schedule, skipping the tick if a visual run is already in progress (so scheduled runs never stack). Schedules are stored in .reepolee/reeqa/schedules.json.

Tool Requirements

Visual baselines and comparisons require external tools installed on the server:

ToolUsed forRequired for
ChromeHeadless browser captureBaseline + compare
vipsImage diffing and croppingCompare only
ffmpegEvidence/recording video productionEvidence recording
ffprobeVideo metadataEvidence recording
sayText-to-speech narration (macOS)Evidence recording

visual_capabilities() checks which tools are available and the /run-tests page shows what is and is not possible on the current server. Command checks need no external tools - they just run shell commands.

File Layout

All ReeQA state lives under .reepolee/reeqa/ in the project root:

.reepolee/reeqa/
    projects.json           # registered projects
    active-project.json     # the active project id
    page-sets.json          # all page sets across all projects
    active-page-set.json    # the active page set per project
    runs.json               # command-check run history
    visual-runs.json        # visual run history (baselines + comparisons)
    schedules.json          # recurring schedules
    baselines/              # captured baseline images, DOM snapshots, DB snapshots
        <project-id>/<page-set-id>/
            manifest.json   # baseline metadata (pages, hashes, capture size, DB snapshot)
            *.png            # full-page screenshots
            *.dom.json       # DOM snapshots (element rects for changed-element mapping)
            *.html           # raw HTML at capture time
            db.snapshot      # SQLite database snapshot (when db:clone-test exists)
    reports/                # comparison report artifacts
        <run-id>/
            *-baseline.png   # the baseline image copied for side-by-side view
            *-current.png    # the current capture
            *-diff.png       # the pixel diff
            *-baseline-zoom.png  # zoomed diff region (baseline)
            *-current-zoom.png   # zoomed diff region (current)
            *-diff-zoom.png      # zoomed diff region (diff)
    profiles/                # temporary Chrome profile directories (deleted after each run)

Queue Integration

ReeQA uses the queue for long-running jobs. Three job types are registered in apps/reeqa/workers.ts:

Job typeWhat it does
reeqa_suite_runExecute a command check (spawn the subprocess, stream output)
reeqa_visual_runExecute a visual capture or comparison (Chrome + vips)
reeqa_evidenceRecord a narrated evidence video for one changed page
reeqa_recordingRecord a clean clip for one unchanged page
reeqa_cancelCancel a running suite or visual job (kill the process / close the browser)

When the queue is unavailable (e.g. the worker process is not running), every job falls back to in-process execution so the dashboard still works. The queue worker is started with bun dev:worker or bun dev:all in development, and as a separate process under PM2 or systemd in production.

Agent Mode

ReeQA supports an --agent flag (requires --dev) that runs the dashboard on a dedicated port (AGENT_REEQA_SERVER_PORT) for automated agent-driven QA workflows. This is used by the Codebuff agent integration to run and inspect QA checks programmatically. In agent mode, the server boots on the agent port instead of the default REEQA_PORT.