Writing verification
Choose unit commands, integration scenarios, or browser tests and retain evidence for the right worktree.
Start with Review your first change to decide which evidence you need. This page explains how to author that evidence with your agent. Choose a test based on the behavior you need to verify. Start with your first run if the project is not connected yet.
| Need | Test type | Execution entry point | Recorded result |
|---|---|---|---|
| Run an existing test command | Unit | redpact tests run --kind unit or Unit controls in the app | Command status, exit code and output |
| Verify a public API and dependencies | Integration | redpact tests run --kind integration or Integration controls | Captured Vitest sources, cases and observed steps |
| Verify browser interactions | Playwright, purpose: "functional" | redpact tests run --kind playwright or Tests → Playwright controls | Scenario and step verdicts, diagnostic attachments |
| Show application states for review | Playwright, purpose: "capture" | redpact tests run --kind playwright or Tests → Playwright controls | Named screenshots, sources and execution details |
A screenshot lets someone inspect appearance. Assertions establish the behavior a test actually checks. Keep both when a change needs functional verification and visual review.
Test-first development
The Redpact skill uses Red → Green → Refactor for testable behavior changes, whether installed as a standalone skill or bundled in a plugin. Choose the relevant Unit, Integration or Playwright functional boundary before implementation:
- Red: Write the regression and run it against the baseline. Observe the intended assertion fail; a nonzero exit, collection error or unavailable environment is not red. A minimal compilation seam must remain behaviorless and be identified in the evidence.
- Green: Implement the smallest change that passes the same intended assertion, then rerun against fresh application inputs. Preserve reviewed assertions and diagnose incorrect fixtures explicitly instead of weakening the test.
- Refactor: Improve structure where useful without changing behavior, then rerun affected tests and required regression checks on the final tree.
Retain the test identity, command or managed run ID, actual failing assertion and observed green result in the task handoff. The parent agent verifies the combined tree when work is delegated. Local red/green results remain separate from connected acceptance evidence; direct shell runs do not create Redpact records. Tests written after implementation do not establish TDD and must be reported honestly.
Documentation and static configuration changes use relevant static/build/review checks without invented failing behavior tests. Visual-only changes still require actual-application captures; see frontend conventions for this repository's viewport and inspection requirements. Execution approval remains a human action in the connected viewer and does not establish code acceptance.
Connect an existing unit command
Add unitTests to the shared .redpact/settings.json. This fragment assumes a project-supplied unit.Dockerfile that installs dependencies and copies the application into /workspace:
{
"unitTests": {
"dockerfile": "unit.Dockerfile",
"cwd": "app/server",
"command": "pnpm test",
"patterns": ["app/server/test/*.test.ts"]
}
}Merge these fields into your configuration, preserving other settings. Paths resolve from the selected worktree. The Dockerfile must provide /bin/sh, the package manager and dependencies. Installation belongs in Dockerfile build steps because Redpact replaces the image's startup command. See configuration.
Open the project's Tests page and choose Unit to target the primary checkout. For a feature worktree, use its Unit execution controls. The Play icon uses the shared project execution scope. Without the opt-in fileCommand, Unit runs the configured command in full. Selecting a source file does not filter execution; patterns determines the catalog and file-command candidates. Use a finite command with watch mode disabled.
Each run gets a fresh container. It does not use host-installed dependencies or require Compose dependency selections. Redpact records the command outcome and bounded stdout/stderr; it does not infer individual test verdicts from text output.
Write integration scenarios
Put managed integration tests and helpers below tests.directory (the Git-tracked root integration/ directory by default). These tests run with Vitest and exercise the actual application's public boundary, such as HTTP. Keep Unit patterns outside this directory. Bind application URLs through configuration, rather than hard-coding allocated ports.
For agent-authored scenarios, ask the Redpact agent to copy the bundled createSteps helper into that directory as steps.ts. Keep it with the captured sources. This test assumes APP_URL is configured for an application with a /api/health endpoint; adapt both to your application:
import { expect, test } from "vitest"
import { createSteps } from "./steps"
test("Application responds successfully", async (context) => {
const step = createSteps(context)
const response = await step("Request application health", () =>
fetch(`${process.env.APP_URL}/api/health`),
)
await step("Verify the application is healthy", () => {
expect(response.status).toBe(200)
})
})Await each step in order. Put the operation or assertion inside its callback and let failures propagate. Use separate steps for meaningful actions and checks. Titles and comments explain intent; observed steps show what execution reached. Write submitted scenario titles and steps in the task's language. Changing viewer language does not translate recorded sources.
Ask the agent to call redpact tests run --kind integration with the absolute working-checkout path, and optional exact test paths relative to tests.directory. Helpers stay in the collected bundle. redpact tests run --kind integration handles integration; unit commands and Playwright sources use their own execution paths.
Read the returned state, then inspect execution through redpact runs show. Preserve its identity and verify evidence belongs to the intended worktree on the connected instance. Running tests directly in a shell does not create a Redpact submission. The project Integration run action collects current sources into a fresh submission; recorded source remains separate from subsequent edits.
Use isolated fixture data. For a save operation, verify readback as well as the immediate response. For a regression, retain the actual failing assertion and rerun against updated application inputs. Collection errors and unavailable applications are setup failures, not proof that the intended assertion failed.
Choose Playwright scope and purpose
Targets have two independent properties:
- Scope controls maintenance:
worktreeis task-specific code;projectis maintained with the project. Omitted scope defaults toproject. - Purpose controls presentation:
captureproduces review scenes;functionalverifies behavior.
Use this layout under the configured scenario root:
ui-tests/
worktree/
captures/
tests/
project/
captures/
tests/Add ui-tests/worktree/ to Git ignore rules. Commit maintained scenarios and helpers. Keep helpers inside the scenario root, and do not make project scenarios depend on worktree drafts.
Configure four non-overlapping entries in playwright.targets:
{
"worktree-captures": { "scope": "worktree", "purpose": "capture", "testMatch": ["worktree/captures/**/*.ts"] },
"worktree-tests": { "scope": "worktree", "purpose": "functional", "testMatch": ["worktree/tests/**/*.ts"] },
"project-captures": { "scope": "project", "purpose": "capture", "testMatch": ["project/captures/**/*.ts"] },
"project-tests": { "scope": "project", "purpose": "functional", "testMatch": ["project/tests/**/*.ts"] }
}This is only the targets value. Configure the application service, port and scenario directory as described in configuration. A file cannot match multiple targets. Each target runs the selected worktree once against its actual application.
Author a browser scenario
Import test and expect from @playwright/test. Redpact supplies the browser runner and application base URL. Relative helpers and Node built-ins are available inside the captured scenario root. Arbitrary additional scenario dependencies and evaluation of a project Playwright configuration are not supported by this runner.
This capture example assumes a Settings route and heading:
import { expect, test } from "@playwright/test"
test("Settings page is ready for review", async ({ page }, info) => {
await test.step("Open Settings", async () => {
await page.goto("/settings")
await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible()
})
await page.evaluate(() => document.fonts.ready)
await info.attach("Settings / General / Initial state", {
body: await page.screenshot({ animations: "disabled", scale: "css" }),
contentType: "image/png",
})
})Use stable, descriptive names such as Page / Group / Capture name. The viewer displays the complete name in its checkpoint selector; / is a naming convention, not a grouping instruction. See browsing captures for the file and checkpoint navigation. Choose useful states such as an empty form, validation error or saved result. Wait for application state and fonts instead of fixed sleeps. Capture the viewport or a meaningful section with enough context to judge the change.
For functional targets, use test.step and assertions on observable results: navigate, submit a form, then verify the resulting state and relevant persistence. Screenshots are optional diagnostics. Functional screenshots do not populate the project's capture gallery.
Open Tests → Playwright → Run Tests and select a worktree and target, or use worktree Playwright controls. The fixed project configuration supplies application and dependency services. The default viewport is 1920 × 1080; settings or run options can override it. Viewing a saved image never starts execution.
Design assertions that answer the task
Write down the behavior before choosing assertions. “The request returned 200” can be useful, but a save feature usually also needs a readback check. A rejected request should check the rejection and, where observable, that no unwanted state change occurred.
| Change | Useful behavior to assert | Additional evidence when needed |
|---|---|---|
| Save a preference | Read the new value through the public API or after reload | Capture the saved state |
| Reject invalid input | Observe the intended error and unchanged stored data | Capture the validation message |
| Filter a list | Matching entries remain and known nonmatching entries disappear | Capture empty and populated results |
| Recover from dependency failure | Observe the application's defined error or retry behavior | Record the configured dependency kind |
Use fixture data with recognizable values so an unrelated default cannot accidentally satisfy the assertion. Keep tests independent of execution order. If a scenario creates data, clean up through the application's supported interface when appropriate, especially when connecting to shared external infrastructure.
A mock-based test verifies your application against the implemented substitute. If the task concerns the real provider's authentication or response contract, a mock result cannot answer that question. State which dependency boundary the scenario actually exercised.
Turn a capture into a maintained scenario
Start a task-specific scene under worktree/captures when it exists only to review the current change. If it represents a state worth checking again, move the final source and required helpers into the maintained project layout, select the project target, and execute it there.
The new execution matters: moving the file does not convert an old worktree record into a project record or populate the maintained gallery. Verify the new target's scope, purpose and screenshot names. Keep functional assertions in a functional target when their continued verdict is part of the project's regression coverage.
Ask for a complete test handoff
For Integration, a useful request is:
Add a focused test for the reported behavior using the project's public API and captured helpers. Demonstrate the intended failing assertion before the fix, then submit a passing run from the actual working checkout. Keep the assertion's intent and report both run IDs, the configured dependency kinds, and any preparation or cleanup issues.
For user-visible UI work, the Redpact plugin defaults to both interaction verification when behavior changes and task-specific visual captures at desktop and mobile viewports. The captures are retained for human review; they neither replace functional assertions nor imply agent visual approval. Name the important states and whether the scenario should remain in the project. For a behavior-only browser change without a rendered UI, say so when requesting functional evidence only.
Finish a task without losing evidence
Both Playwright scopes retain executed scenario sources and artifacts. Worktree drafts remain editable during the task. After the final recorded execution and resource cleanup, ask the agent to perform the explicit draft cleanup operation. The viewer has no draft-cleanup button. Successful runs and merges do not automatically remove drafts; retained evidence remains available.
Maintaining a scenario long-term requires explicitly moving it into project and updating
imports or target matches as needed.
Continue to results and screenshots, troubleshooting, or the documentation overview.
Execution requests may choose executionScope: "all" | "changed". Keep tests and
helpers in their owning runner directory, and use all when implementation changes
need regression coverage: changed selection does not analyze import dependencies.
Exact Integration tests paths require an effective all scope. See
execution selection for Git, helper and empty-selection rules, and
configuration for the explicit Unit file-command contract.