host-stack-infrastructure
Use when adding or changing how a browser or memory-leak suite is executed — a host-built export instead of the Docker prod stack, a new executor mode, or a Makefile switch. Triggers on "run e2e witho
SKILL.mdplugins/react-frontend-sdlc/skills/host-stack-infrastructure/SKILL.md
GitHubHost Stack Infrastructure
Profile keys consumed
make.test_e2emake.test_visualmake.test_memory_leakmake.start_prodcapabilities.visual_testing
Every suite runs through the profile's make target map; skip a dependent
step with a recorded note when its key maps to null, and skip the
baseline-drift reasoning entirely when capabilities.visual_testing is
false. The browser-install and baseline-update targets have no logical
key — name them by purpose and resolve them in the repository's Makefile.
Overview
A new execution environment gets its own explicit switch. Never derive one from an ambient variable and never overload an existing one: visual baselines are environment-bound, so a silent mode change moves a suite off the containers its snapshots came from and produces diffs that are pure environment drift.
When to use
- Adding a way to run the browser suites without a Docker daemon.
- Introducing or renaming a Makefile switch that selects an executor or a stack.
- Wiring a workflow to a second execution context.
- Investigating visual diffs that appear locally but not in the pipeline, or the reverse.
- Not for: picking which suite to run, or triaging an ordinary spec failure.
Applicability by repository shape
- React SPA shape (feature modules under the source root, a bootable app, an aggregate
CI target): partial — the same principle, spelled
ENV ?= prodwith anENV=devbranch on the targets mapped bymake.test_e2e/make.test_visual; dev-mode snapshots live intests/visual/__snapshots__-dev/under a separatechromium-devproject so they can never overwrite the production baselines produced againstmake.start_prod. - Next.js app shape (routed pages, no aggregate duplication gate): yes —
HOST_STACK(default0, accepting1/true/TRUE) drives a host-stack shell script;EXEC_MODE(containerdefault,hostopt-in) is a second, independent switch. - Component-library shape (Storybook-first, no bootable app, published package): no — every suite runs in Docker Compose; no host mode exists.
Core pattern
The ambient CI variable is exported into every step by the runner, so a switch derived from it is
on in the pipeline whether or not anyone asked. Two separate concerns each get their own name — the
spellings below are the Next.js shape's:
make test-e2e # make.test_e2e — default: the Docker prod stack, everywhere
HOST_STACK=1 make test-e2e # explicit opt-in, per invocation, no daemon needed
HOST_STACK=1 make playwright-install # fetch the browsers the host mode needs (no logical key)
Design rules that make the switch safe:
- One switch, one concern. Selecting the executor for lint/test tooling and selecting the stack the browser suites hit are different questions; a single flag answering both is ambiguous.
- Enum, not boolean, when there are named alternatives, and an unrecognised value is a hard
$(error). An escape hatch that can be mistyped into silence is the same defect as deriving it fromCI. - Accept the truthy spellings (
1,true,TRUE) so a workflow writingtruedoes not silently take the default path. - Refuse, do not degrade. Baseline-writing targets — the repository's visual-baseline update
target — stay unavailable in the alternate mode. Host font rasterisation differs from the pinned
Playwright image, so a snapshot update run there would rewrite every baseline and red the
container-run gate behind
make.test_visual; the target exits with an explanatory error before its prerequisite builds anything. - Turn an unreachable daemon into the hint. The first target that touches Docker on every
browser path — including the memory-leak lane mapped by
make.test_memory_leak— prints the exact opt-in command instead of a raw connection error.
Quick reference
- The container image stays the baseline source of truth; the alternate mode is opt-in only.
- Pin any value the container build bakes in (a served URL, a port) into the alternate build too, or specs asserting on it diverge between modes.
- Leave unrelated environment variables alone in the alternate branch — changing one can un-skip specs that the default mode skips.
- Exercise both modes locally before proposing the change.
Common mistakes
- Deriving a mode from
CI— the runner sets it everywhere, so the pipeline silently takes the alternate path. - Reusing an executor switch to also mean "different stack" — split them.
- Allowing snapshot updates in the alternate mode — the resulting baselines fail the gate that owns them.
- Accepting only
1and treatingtrueas the default — a workflow spelling it that way gets the wrong stack with no signal.