uat-env

star 4

This skill should be used when verifying code changes with an in-browser UAT or E2E test in a project worktree — it starts an isolated, per-worktree Apple container environment (app + database + supporting services) on a deterministic localhost port, and tears it down when verification is complete. Trigger phrases include "run a UAT", "verify this fix in the browser", "spin up the test environment", "start the dev server for this worktree", "uat-env".

arlenagreer By arlenagreer schedule Updated 6/12/2026

name: uat-env description: This skill should be used when verifying code changes with an in-browser UAT or E2E test in a project worktree — it starts an isolated, per-worktree Apple container environment (app + database + supporting services) on a deterministic localhost port, and tears it down when verification is complete. Trigger phrases include "run a UAT", "verify this fix in the browser", "spin up the test environment", "start the dev server for this worktree", "uat-env". version: 1.0.0 author: Arlen Greer date: 2026-06-12

uat-env — Per-Worktree UAT Environments (Apple Containers)

Purpose

Manage ephemeral, isolated development environments for in-browser UAT cycles using Apple's container CLI. Each git worktree gets its own container stack (app server, database, cache, mail capture) on a deterministic port block, so two branches of the same codebase can run UATs simultaneously with no shared state — no port conflicts, no shared database, no shared dev stack.

All container lifecycle operations go through one deterministic script: ~/.claude/skills/uat-env/scripts/uat-env. Never improvise raw container run commands for UAT environments — the script handles image builds, startup ordering, health checks, service IP wiring, and naming.

When to Use

  • Code edits are ready to verify in a real browser (UAT, E2E, visual check)
  • A Playwright or /browse session needs a live app instance for THIS worktree
  • The user asks to test a fix while another branch's environment may be running
  • A clean-baseline environment with fresh seed data is needed

Do NOT use for: full-stack work needing the project's complete docker-compose profile (LocalStack, WireMock, Selenium, pgAdmin) — those projects keep compose for that; this skill covers the minimal UAT subset only.

Prerequisites

  1. Apple container CLI installed (macOS 26+, Apple silicon) and apiserver running (container system start)
  2. The project has a .uat-env.json at its repo root (per-project configuration)
  3. Run uat-env doctor to verify all prerequisites in one shot

If the current project has no .uat-env.json, offer to create one — see references/configuration.md for the schema and a worked example (SoftTrak).

Core Workflow: the UAT Iteration Loop

SCRIPT=~/.claude/skills/uat-env/scripts/uat-env

# 1. Start (or reuse) this worktree's environment
"$SCRIPT" up                  # prints: URL: http://127.0.0.1:<port>

# 2. Run the UAT against the printed URL (browser, Playwright, /browse, /qa)

# 3. Edit code on the host — source is bind-mounted; the running container
#    sees changes. Re-run the UAT. Do NOT restart containers per iteration.

# 4. When verification is complete:
"$SCRIPT" down

Key behaviors to rely on:

  • Per-worktree network isolation (macOS 26+). Each stack runs on its own named container network (uat-<project>-<worktree-slug>), so containers from different worktree stacks cannot reach each other. On macOS 15 or hosts without container network support, the script warns and falls back to the shared default network — stacks still work, but are not network-partitioned. Published-port access on 127.0.0.1 is unchanged either way.
  • up is idempotent. If the stack is already running, it reports status and the URL. Safe to call before every UAT.
  • The edit→verify loop needs no container restarts. Source directories are bind-mounted. Only restart (down then up) if the app process itself must reload something it cannot hot-reload (Gemfile changes, new migrations needing db:prepare).
  • Baseline reset (APFS clone): uat-env snapshot captures the seeded db volume as an APFS copy-on-write clone baseline — instant, zero-space. uat-env reset restores the db volume to that baseline in place (stops→clone-restores→restarts the db container). Baselines are per-worktree (namespaced by PREFIX), stored on the same APFS volume as the container volumes root. Run snapshot once after seeding; use reset between UAT iterations to return to pristine data instantly.
  • up --fresh smart path: when a baseline exists, --fresh restores it via clone instead of purging volumes (much faster). When no baseline exists, falls back to the unchanged purge+reseed path.
  • Seeding: uat-env seed runs the project's configured seed command inside the app container.
  • Two branches in parallel: run the same commands from each worktree directory. Ports are derived from the worktree path, so each branch gets a stable, distinct URL. uat-env url prints it without starting anything.

Command Reference

Command Effect
up [--fresh] Build image if needed, start infra→app→workers in order, wait for health, print URL. --fresh: if a baseline exists, restores it via APFS clone (fast); otherwise purges data volumes for full purge+reseed.
down [--purge] Stop and remove this worktree's containers. --purge also deletes data volumes.
snapshot [svc] Capture the current seeded db volume as an instant APFS clone baseline. Stops the db container for a clean Postgres checkpoint, then cp -c clones volume.img. Optional svc overrides the configured .snapshot.service (default: db).
reset [svc] Restore the db volume to the captured baseline via copy-on-write clone (instant; stops→restores→restarts the db container). Errors if no baseline exists — run snapshot first. Optional svc overrides configured default.
status Show this worktree's containers and URL
url Print the app URL (stable per worktree)
seed Run the configured seed command in the app container
exec SVC CMD… Run a command in a service container (e.g. exec backend bundle exec rails c)
logs SVC Follow logs for a service
doctor Verify CLI, apiserver, config, and port availability

Session Etiquette

  • Start the environment lazily — only when a UAT is actually about to run
  • Always down at the end of a UAT session (wrap-up, task completion) unless the user says to keep it running
  • If up fails, run doctor and report its output rather than retrying blindly
  • Surface the URL to the user whenever the environment starts — they may want to watch the UAT
  • First up in a project builds the image and installs dependencies; warn the user it can take several minutes. Subsequent starts are fast.

Troubleshooting

  • App container starts but edits aren't picked up: only affects evented file watchers (inotify does not propagate over the virtiofs mount). Stat/mtime-based watchers work as-is — verified live on SoftTrak (Rails 8 default FileUpdateChecker, no listen gem): host edits to views and code reload per-request with no polling config. If a project DOES use an evented watcher: for Rails remove the listen gem or set config.file_watcher = ActiveSupport::FileUpdateChecker in development.rb; for Node bundlers set CHOKIDAR_USEPOLLING=1 / Vite server.watch.usePolling: true in the project's .uat-env.json env block.
  • Port reported in use: another process owns the derived port — doctor detects this. Adjust base_port in .uat-env.json if it collides with a fixed service.
  • could not resolve IP: the container inspect JSON schema may have changed in a newer CLI release — inspect manually and update ctr_ip() in the script.
  • Memory growth over long sessions: Apple containers don't return freed memory to macOS until restarted; down/up long-lived stacks periodically.
  • reset says "no baseline": run uat-env seed to populate data, then uat-env snapshot to capture the baseline. After that, reset is instant on every subsequent iteration.
  • snapshot/reset refuses: "not same APFS volume": the BASELINE_ROOT path (under ~/Library/Application Support/com.apple.container/volumes/.uat-baselines) must sit on the same APFS volume as the container volumes root. This guard prevents cp -c from silently falling back to a slow full copy. If your home directory is on a different volume than /System/Volumes/Data, contact the project maintainer to configure an alternate baseline location.
  • snapshot/reset stop and restart the db container: volume.img is a block device attached to exactly one container VM at a time. Both commands stop the db container before cloning/restoring and restart it after, so the consuming container is momentarily unavailable during the operation (typically a few seconds).

Bundled Resources

  • scripts/uat-env — the lifecycle script (single source of truth for container operations)
  • references/configuration.md.uat-env.json schema, placeholder tokens, and the SoftTrak example with porting notes from docker-compose
Install via CLI
npx skills add https://github.com/arlenagreer/claude_configuration_docs --skill uat-env
Repository Details
star Stars 4
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator