Red Pact

Execution and environments

Test runtimes, evidence, manual containers, limits and cleanup.

Runtime ownership

EntryRuntime and inputsResult
UnitProject Dockerfile and configured command or exact file command in a temporary containerCommand status, exit code, stdout/stderr and cleanup status
IntegrationContainerized Vitest against a fresh Testcontainers Compose application environmentImmutable submitted source, cases, steps and run/environment records
PlaywrightContainerized browser against a temporary application environmentFunctional results or capture evidence, optional video/trace
ContainerExplicitly started manual project application environmentInspection endpoints and lifecycle state; not test evidence

Unit has no host-command fallback. Its Dockerfile must copy sources and dependencies into /workspace, provide /bin/sh, and install during build. cwd selects the command directory. Dockerfile ENTRYPOINT/CMD do not supply Redpact's supervisor. The selected source file in the viewer does not filter the configured command; execution scope uses the explicit contract below. Results do not invent per-case verdicts from an exit code.

Integration collects current checkout sources from Project Tests and worktree review. redpact tests run --kind integration collects fresh sources for the supplied path. Submission identity and current file contents must remain distinguishable in the viewer. Test authoring describes supported source collection and assertions.

The three entries are separate execution contracts. redpact tests run --kind integration admits only Integration submissions. Unit runs start from the Unit tab or redpact tests run --kind unit and execute the complete configured command; their exit status is not an Integration case result. Playwright runs start by selecting a declared functional or capture target in the Playwright UI/API or redpact tests run --kind playwright. Their screenshots, UI-review artifacts, execution record and cleanup stay with that Playwright run rather than an Integration submission. See MCP interfaces for approval limits, recorded evidence lookup and public lifecycle boundaries.

The viewer presents a common progress strip for all three run types in their result views. It shows the current preparation, execution, cleanup or finished stage and elapsed time; an active Unit or Integration run also appears beside its Run control. Unit distinguishes source capture, Docker connection, container build and command execution. Integration distinguishes queueing, environment preparation and test execution. Playwright distinguishes application preparation, browser startup and browser execution. These labels come from the durable facts already recorded on each run, so they can skip a short stage between refreshes. They are not individual timed events or a stream of live command output. Errors and cleanup results remain on the selected run.

For agent-mediated verification, direct Docker/Compose, Vitest or Playwright CLI commands are local diagnostics, not Redpact execution evidence. Start each selected path through its entry above so the connected worktree retains its result and cleanup.

Approval and accepted inputs

MCP execution under Ask creates a durable review before starting project code. Unit records the command/file-command specification, execution selection and a digest of the inputs its container will copy. Playwright records the target, purpose, effective viewport, settings digest, application fingerprint and test-directory digest. Requests retain their captured policy when future defaults change. The same reviewed request can be approved in the chat App or connected review screen; see request approval.

Approval revalidates the recorded specification and digests. Changed settings or sources require a new request. Unit verifies the actual copied inputs before connecting to Docker or building the image. Playwright verifies the captured test directory at admission and the approved application fingerprint before recording its environment; the adapter checks its copied inputs again before provisioning. A failure is recorded without silently substituting newer inputs. A pending review is not an executed test, a recorded TDD red, or human acceptance of the implementation.

Fingerprints cover the inputs described below, not every possible external effect. Mutable image tags, external build contexts, host mounts, file-backed resources and remote services retain their existing Compose boundaries. Approval does not turn those inputs into immutable snapshots or prove runtime readiness.

Temporary lifecycle

A run is queued before application preparation. Its environment binding may appear later. Each execution owns a fresh environment; it cannot reserve another run's completed environment. Public standalone preparation and environmentId reuse are not supported. A run environment proceeds through preparation, readiness, use, completion and removal, retaining diagnostics on failure.

Temporary workers retain live Testcontainers container, Compose and network handles through evidence collection and teardown. Normal teardown uses stop(), down() and Network.stop(); Ryuk provides fallback cleanup if a worker disappears. Ownership labels and daemon identity remain for recovery and cleanup retries when live handles are unavailable, rather than replacing the normal Testcontainers lifecycle. The pinned library normally reuses a Ryuk session across processes. Redpact's patch gives each temporary worker an isolated Ryuk container/session, so another active execution cannot delay cleanup after that worker dies. Shared runner images and the persistent main environment remain outside this per-worker cleanup scope. Abnormal-exit cleanup observes Ryuk's reconnection grace period; it is not immediate.

Success, assertion failure, preparation failure, cancellation and interruption all request cleanup. Stop blocks admission, confirms execution cancellation, then removes owned containers, networks, volumes, per-run Unit images, temporary Compose build images and captured runtime source. Integration and Playwright keep their shared runner images; temporary application images belong to the run environment and are removed with it. Preserve metadata, logs, results and shared Docker build caches. A cleanup failure is stop_failed, distinct from the test verdict, and remains retryable. Startup reconciles labelled resources and removes interrupted environments; it never reruns tests automatically.

Every Compose build uses a dedicated environment-UUID image tag, including builds with an authored image. Generated configuration clears build.tags so execution cannot overwrite authored or shared tags. Build images carry the instance ownership label, retaining reusable final layers across environments; durable records identify the environment's dedicated tags for cleanup. Other build options and pull policy remain unchanged. Authored tags and external networks/volumes are not cleanup targets. Named resources are removed only when their labels identify this environment. Anonymous volumes are removed with their owned container. Cleanup never treats a mounted volume as owned merely because an application uses it, and does not re-evaluate Compose expressions. Unit build images carry owner/run labels so cleanup also finds them when container startup fails before an image ID is recorded.

The shared Playwright runner installs only Chromium and its system dependencies; Firefox and WebKit are not included. The self-E2E application uses a Node slim runtime without browsers. Browser scenarios live in ui-tests/project/tests and execute in the separate Playwright runner, including test-code diff and capture viewport checks.

Playwright reuses its shared browser image when the Dockerfile and bundled reporter assets have the same digest. Warm captures skip the browser image build. Each capture still starts a fresh browser container and fresh application environment; their containers and application volumes are removed after execution. Runner image upgrades and Docker build caches are separate from per-run cleanup. Project Dockerfiles determine application image size; use separate build/runtime stages to avoid retaining build tools and caches in runtime layers. This repository's e2e/Dockerfile uses a separate build stage and the existing runtime packager, keeping production server dependencies, built UI and the editable Git fixture while excluding web development dependencies and the build stage's package store.

Redpact does not prune unrelated Docker resources. Shared-local and remote services are independently managed and are not stopped by Redpact; tests own their fixture isolation. Normal shutdown awaits execution and cleanup. Abrupt termination may leave resources until successful recovery.

Manual Container

The project Container screen defaults to the configured main checkout. redpact environment uses its actual path worktree; HTTP Container routes accept worktreeId to select one. Directory projects use their connected root. All targets use the fixed shared settings. Each worktree can own one manual environment, independent of test environments. The main-checkout environment stays alive across tests, idle periods, agent completion and Redpact shutdown until explicit Stop or Restart. Other worktree environments use the temporary Testcontainers session lifecycle. For the main environment, on server restart ownership and observed Docker health are reconnected without recreating services or treating this as new test evidence. Interrupted preparations remain failed; interrupted stops retain cleanup ownership for retry. Docker unavailability is distinct from an explicit successful stop.

Stop removes all owned containers, anonymous and dedicated named volumes, networks, environment build tags, source snapshots and generated runtime files. Preparation and service logs and historical environment records remain. Dedicated names use the environment UUID; authored nonexternal network/volume names and build image tags cannot collide with another worktree or overwrite an existing project tag. External volumes/networks, bind mounts, pulled base images and unrelated tags remain outside environment ownership.

BuildKit's content-addressed internal cache and shared image layers remain shared Docker resources. Redpact does not prune them or promise that removing a tag frees every byte of its reported image size. Builds use an instance ownership label rather than a fresh per-environment image label so identical final layers remain reusable. Generated Compose files stay outside build contexts. A newly changed source can add shared cache; dedicated images/tags and runtime files are still removed. Explicit cache exports declared by the project remain project-managed shared resources. Cleanup failure is not stopped: retained ownership and diagnostics allow an explicit retry, and restart waits for successful teardown.

Local edits show input changes but never hot-reload or restart the session. Restart removes old owned resources before capturing current inputs; failed cleanup blocks replacement. Stop remains available when source/settings are unavailable. Reverting inputs clears freshness differences. Unknown inputs show a diagnostic, not a false up-to-date state. HTTP endpoints are checked for host reachability before Ready; any HTTP response proves reachability, not application health.

Compose and source capture

Compose is the application execution definition. Redpact applies the configured service selection and explicit dependency environment bindings, records ownership, and adds runner connectivity where the service's network mode permits it. It does not impose a managed Compose subset: host mounts, privileges, custom container names, external resources, env_file, configs/secrets and advanced build options are not rejected as Redpact policy. Published ports use automatic host allocation except explicit main-manual dependency hostPorts; see configuration. Build image tags and nonexternal network/volume names are environment-owned. Custom container names and other authored host resources can still conflict; native errors are retained.

Compose/Testcontainers handle native configuration, build, startup and wait errors. A healthcheck is optional. When present, its reported health is observed; otherwise a running service is accepted. Redpact does not add a host HTTP reachability gate; actual HTTP/DB behavior is established by test assertions. Completion jobs use service_completed_successfully. The Redpact settings schema, conflicting dependency bindings, input identity, cancellation and cleanup ownership checks remain separate from Compose policy.

The patched Testcontainers Compose config API resolves merged inputs and paths for capture, not an additional policy validator. It bounds command duration/output, accepts cancellation, and invokes native docker compose config with the supplied minimal environment. Resolved service definitions preserve Compose options before Testcontainers starts the selected services. The server passes its minimal process environment and explicitly supplied bindings, not arbitrary server credentials. Project .env and env_file are interpreted by Compose. Use a local Unix Docker socket and a Compose version supporting the constructs your project declares.

Capture copies selected project-local build contexts with Docker ignore precedence. External, remote and linked build inputs remain at their authored locations and are handled by Compose/BuildKit; Redpact does not read or copy their contents. Host mounts and file-backed resources also remain external runtime inputs. These inputs are not an immutable captured application snapshot. Image-only services do not scan source. Paths, contents, modes and link text of captured files contribute to identity. Unit excludes host dependency directories, including node_modules, .pnpm-store, .venv and venv, before Docker build; links inside excluded directories are not followed or copied. Input capture has no fixed file-count/total-byte ceiling; submitted-test limits are separate. Concurrent file edits, external downloads and mutable image tags are not a complete immutable application snapshot. Queued/input changes can invalidate preparation.

The pinned Testcontainers patch collects a final Compose log snapshot before failed-start cleanup, including failures before readiness waits begin. Re-evaluate it when upgrading the library.

Failure diagnostics

Runner log consumers are attached before container startup. Build/start diagnostics, command stdout/stderr and Compose service output are collected separately, with bounded output and literal environment-value redaction across stream chunks. Truncation and collection failures remain visible in the retained logs. Command output is saved before copying result archives, so a missing or unreadable archive cannot erase the original failure output. Preparation errors retain their diagnostic message and service excerpt; Unit preparation failures retain stdout/stderr on the run for viewer and copy actions. Cleanup failure remains separate from the original execution verdict. The recorded execution review provides an Execution logs disclosure. It reads the same retained diagnostics as Copy log and the MCP log response, including Playwright stdout/stderr. Opening it does not rerun the command or require its container to exist.

Runner connectivity

Each execution creates a labelled runner network. Integration and Playwright join that network in their own network namespaces. Managed services have <service>.redpact.test DNS aliases; browser baseURL uses the configured scheme and port with that hostname. Applications must accept that origin and configure their own CORS, cookies and TLS accordingly. Redpact does not disable certificate checks or browser security, rewrite remote origins, or treat a successful host request as runner reachability.

Declare shared-host URLs explicitly with host.docker.internal. The runner supplies a host-gateway mapping. Docker Desktop loopback access is tested from actual runners; Linux host-gateway access does not make a loopback-only listener reachable. Configure a reachable host listener or an explicit project-owned forwarding service and verify from the consuming runner. Never replace a runner URL with localhost blindly. Remote services keep their real URLs and authentication. Shared-local and remote data are not isolated by per-run Docker networks; fixtures must use independent data.

Playwright evidence

Targets declare maintenance scope: "worktree" | "project" and purpose: "capture" | "functional". A target executes the selected checkout once; old Before/After baseline descriptions are not the current execution model. Capture is visual evidence; functional assertions establish tested behavior.

Playwright lists current worktree capture drafts and changed project capture files even before execution or when no PNG was recorded. Source presence keeps the tab, file selection and execution action available. The screenshot viewer's local Mobile switch filters recorded Desktop/Mobile images, not the source list; it does not occupy the shared app header. Images come from the latest matching execution for the selected size; no image in that execution means no substitution of an older image. Recorded CSS viewport width classifies each PNG at 768px; missing provenance stays visible in both modes with an explicit label. Image pixels, filenames and current run defaults do not establish viewport provenance. Project Captures lists declared capture sources before execution and separates each file's Test Code from its Screenshots; an empty Screenshots tab does not hide executable capture code. Each selected file shows a horizontal strip of named thumbnails above the large image. Click a thumbnail or use the radio group's arrow keys to select a checkpoint without opening a dropdown. The strip scrolls on narrow screens. The selected image fills the mobile content width and scrolls vertically instead of shrinking into the remaining height; its run link remains available below the image. Project Tests retains Captures, Tests and Runs independently of changed-file review.

The viewer has no Clean up worktree code action. Agents explicitly request draft cleanup through POST /api/worktrees/:id/playwright/cleanup-worktree after the final recorded execution. It refuses unrecorded draft changes or unfinished execution/resource cleanup. Removing task drafts never removes retained sources, images or execution history. This is separate from the viewer's test environment lifecycle and cleanup controls.

Runs with nonempty environment values omit trace archives. Images, videos and submitted source are not credential-scanned. Literal environment-value redaction in text is not a hostile-code sandbox.

Limits and verdicts

Instance defaults are environmentConcurrency: 2, testResources.memoryMiB: 2048 and testResources.timeoutSeconds: 600. Allowed ranges are 1–4 concurrent managed environments, 64–1048576 MiB and 1–86400 seconds. New operations capture new settings. Per-test/hook timeouts remain separate; runner declarations defines their default and allowed range. For example, tests.timeoutMs: 180000 allows a test to run for three minutes, but a shorter instance budget still stops the runner first. Explicit Vitest test/hook timeouts override the default.

Integration test/hook timeout errors retain Vitest's original message and add the effective limit, elapsed case duration, test/hook stage and last interrupted named Step when present. Module and nested suite-hook failures report elapsed module duration and remain execution errors even when their tests were skipped or passed. These are execution errors, not assertion failures. Runner wall-clock limits instead retain resource-limit evidence and stdout/stderr; preparation limits are separate.

Unit, Integration and Playwright use Docker memory/swap limits and an external wall-clock timer. Integration also bounds the V8 heap. Its submitted package installation executes inside the runner and consumes the same execution budget. Limit violations are execution errors, not assertions or cancellation. Application/dependency containers, image builds and direct shell tests are outside this runner budget.

Distinguish passed/failed assertions from collection, configuration, environment, execution, cancellation, interruption and unknown outcomes. Parsing a test is not running it. Missing reports after termination do not imply success. Finished runs can still require cleanup.

Portable fixtures

REDPACT_CONNECTIONS_FILE points to a private per-run JSON file with version: 1 and services.<name>.ports.<containerPort>: { host, port }. Hosts are <service>.redpact.test aliases and ports are internal container ports. Services without published ports have empty maps. No credentials or container variables are included. Project fixtures create their own HTTP/DB clients and clean them up; no Redpact SDK or runtime API calls are needed. Both runners receive the same explicit tests.env bindings.

For extra drivers, submit the supported root package manifest and frozen pnpm lockfile with helpers. The Order Desk provider and fixtures demonstrate portable clients. Outside Redpact the example accepts TEST_CONNECTIONS_FILE; this does not introduce a host application fallback inside Redpact.

Implementation: execution workflow, stop workflow, Unit workflow, Playwright record workflow, resource schema.

Testcontainers delegation

The pinned testcontainers@12.1.0 and its checked-in patch own Docker operations: Compose config/build/start, image build/pull/inspection/selective removal, container and network discovery, volume inspection/removal, logs, exec, resource updates, OOM events, archives and ordered stop/removal. Production adapters do not invoke Docker CLI or Dockerode directly. The runtime client resolves existing IDs after worker exit or restart and supplies the selected local socket and daemon identity.

The patch extends existing library clients where the pinned release lacks APIs. It adds network ownership labels and a Compose callback before failed-start cleanup so logs can be collected before resources disappear. It also isolates worker Ryuk sessions without changing the library's default reuse behavior for other callers. It closes build-context streams before returning, including when the Docker build API rejects before returning a response. It propagates build-stream failures even when an earlier image tag exists, returns an inspected image ID after building, and honors autoCleanup: false inside failed container waits. Finite log snapshots propagate retrieval errors and handle TTY and multiplexed stdout/stderr. Exec supports cancellation and bounded output; the runtime retains responsibility for stopping the container and recording limits and verdicts. Compose config is bounded to 30 seconds and 4 MiB by default.

Integration and Playwright runner tags include the digest of their complete bundled build inputs. Reuse requires a matching input label, and execution uses the inspected image ID. Build errors never fall back to a previous tag. Unit images retain per-run owner labels and enable BuildKit for authored Dockerfile features such as cache mounts. Shared runner Dockerfiles use the library default builder. Build layer caching stays enabled; automatic image deletion is disabled so Redpact applies the ownership and retention rules above. Mutable upstream images and external downloads remain outside the immutable source guarantee.

Redpact owns approval, input/evidence identity, durable state, resource ownership, retention and cleanup retries. Services with healthchecks wait for health; other running services do not wait for published ports. This does not establish HTTP/DB readiness, which belongs in test assertions. Failed startup preserves containers until evidence collection and owned-resource cleanup. Archives are retrieved through Testcontainers and extracted with host tar.

Reassess and test the patch when upgrading Testcontainers, including installed-package bundling. These extensions are repository-maintained library changes, not claims that unmodified upstream 12.1.0 provides these contracts.

Network allocation and recovery

Integration and Playwright allocate one runner bridge in addition to Compose's non-external application networks. A default Compose application therefore normally uses two subnets per execution. Authored networks are preserved to retain their IPAM, isolation and driver semantics. External networks are never owned merely because a service joins them. Manual environments retain their networks until stopped.

Docker allocates subnets from its daemon address pools unless an authored network specifies IPAM. A pool with prefix /16 subdivided into /24 networks has 256 subnets before existing allocations and routing conflicts are considered. Address exhaustion alone does not identify a Testcontainers defect or prove a leak: inspect retained environments, the daemon's default-address-pools, allocated subnet sizes, and host/VPN routes separately. Overlapping authored subnets are a different error. Preparation logs retain the daemon error and add guidance for these two cases.

Cleanup discovers resources by the exact instance owner and environment labels, then rechecks those labels before removal. It does not require a current Compose project label: an older resource can retain valid ownership with a missing or stale Compose label. Such networks previously escaped discovery and could leave an allocated subnet after cleanup reported success. Missing ownership is not permission to delete a network. Containers are removed before networks; failed removal preserves resource IDs and the daemon reason in cleanup.log and the environment error. The record refreshes its remaining resources when the daemon is reachable, stays stop_failed, and supports another Stop attempt. If inspection is unavailable, previously known resources remain recorded rather than being declared absent. Startup also checks previously stopped records with a known daemon identity for owned leftovers. A manual record with retained resources becomes stop_failed and requires explicit Stop; transient execution resources are cleaned up automatically using their durable environment records. Records, submissions and logs remain after removal. Resources with no durable record or verifiable ownership require explicit investigation and are never globally pruned.

Before changing daemon pools, retry cleanup for owned retained environments and verify the resulting network list. Increasing pool capacity does not fix a cleanup failure, and global network pruning can delete other projects' resources.

Selecting all or changed tests

Execution selection is independent of catalog scope and Playwright maintenance scope. Agent API/CLI requests can use executionScope to override the shared default. Viewer Run controls use that default without a per-run selector; recorded scope remains visible in approval/result evidence. all selects the current Integration test directory, Unit file patterns or the selected Playwright target. A legacy Unit shell command without fileCommand discovers its own files; its record explicitly leaves that file list unknown.

changed compares committed HEAD with the single merge-base of HEAD and the local tracking.mainBranch. It unions committed changes, staged changes, unstaged changes and nonignored untracked files. Index changes remain changes even when working content was restored. Renames include old and new paths, but only existing catalog test files execute; deleted tests do not execute. A missing main branch, unborn HEAD, missing/ambiguous merge-base or Git read failure rejects changed execution. There is no silent fallback when the comparison is unavailable.

Changed helper/fixture files under the runner's test directories select all files in that runner/target. Unit patterns without a fixed directory prefix (such as foo*.test.ts) treat the project root as the helper directory and conservatively select all on non-test source changes. Other targets' declared test files do not count as helpers. Git-visible .redpact/settings.json, package manifests/locks, .gitignore, TypeScript configuration and Vitest/Vite/Playwright/Jest configuration at the project root and runner directory also select all. Records retain requested changed, resolved all and supporting_inputs rather than disguising this as a changed-only run. Implementation-source edits outside those directories do not infer affected tests; this is file selection, not dependency analysis. Choose all for a full regression.

Integration's explicit tests list combines with all and is recorded as explicit; it conflicts with changed, including a shared changed default. Helpers remain in the immutable submission. Playwright always selects inside one declared target and keeps helpers in the captured source bundle. Unit uses the authored file-command contract. Zero selected tests rejects admission with “No tests selected”, starts no container and creates no passing execution.

New submissions, Unit/Playwright runs and approvals retain requested/resolved scope, comparison revision, reason and selected files. Approval/result views display this selection. Unit and Playwright revalidate selection as well as existing source/settings fingerprints before admission; a changed base or selected file set requires a new request. Integration retains its immutable submitted sources. Historical records have unknown selection rather than an invented full-suite claim. Project and worktree Integration Run controls collect the current checkout; explicitly rerunning a recorded submission still uses that original bundle.

On this page