The default is report-only. A job fails only when
stacknext compare is given a
matching --fail-on option.What the job needs
- Node.js 20.9 or newer.
- pnpm or npm with the repository’s lockfile.
- The app built and able to start in the runner.
- Playwright browsers installed (
pnpm exec playwright install --with-deps chromium). - One Playwright worker.
- A baseline whose recorded environment matches the candidate runner.
- At least three verified samples per scenario.
GitHub Actions example
This example assumes the app directory isapps/my-app, the Playwright config
starts the app through webServer, and a baseline file is available from a
previous baseline job or artifact. Adjust paths and package manager commands.
--fail-on regression makes the compare step exit 1 only for a regressed
comparison. Use --fail-on inconclusive to fail when evidence is insufficient,
or --fail-on any to fail on either.
The capture step exits non-zero when the child test command fails, and exits
3 when the child succeeds but telemetry is incomplete. Do not ignore that
exit code.
Where the baseline comes from
The baseline must be produced by an earlier capture and saved withstacknext baseline save. Pick one approach:
Same-runner baseline job
Run both the baseline and candidate captures in one workflow on the same runner type. This is the most reliable option because Node.js, Next.js, Playwright, OS and database versions are identical.- Check out the known-good revision in a separate directory or worktree.
- Install, build and start that revision.
- Capture it and save a baseline.
- Check out the candidate revision in the primary workspace.
- Capture the candidate and compare.
Stored baseline artifact
Save a baseline from the main branch once, upload the JSON as an artifact, and download it into later PR jobs. Downloading from a different workflow run needs the sourcerun-id and a token with actions: read:
Reviewed baseline file
A team can keep a reviewed baseline JSON in a directory outside.stacknext,
for example ci-baselines/, and copy it into the workspace at the start of the
job. Review it before committing; it contains redacted route, span, query, error
and environment data. Never commit .stacknext runtime state or tokens.
Capture wiring
App started by Playwright webServer
stacknext capture starts an ephemeral collector and passes the run id,
collector URL and token to the child. Playwright’s webServer inherits the
environment and starts the app:
App started by stacknext dev
If the workflow starts the app separately, run stacknext dev in the app
directory and wait for both the collector and the app before capturing. Capture
attaches to the live collector when .stacknext/run.json is valid.
.stacknext/run.json:
Thresholds and environment
Keep the defaults unless you have measured your own noise. The comparison requires three verified samples per side by default; duration regressions must cross both 20% and 25 ms. See comparison methodology for the exact ordering. Pin the runner image and versions. The comparison records Node.js, Next.js, platform, architecture, runtime, enabled adapters, instrumentation flags, SDK version, Playwright version and project name, and defaults toinconclusive
when they differ. Do not build a baseline on macOS and compare it with a Linux
candidate unless you explicitly accept --ignore-environment and its warning.
Artifacts and privacy
Upload the capture JSON,.stacknext/runs/run-*.json,
.stacknext/baselines/base-*.json, .stacknext/comparisons/cmp-*.json and the
Playwright report if your project produces one.
The records contain redacted scenario titles and file paths, normalized routes
and SQL shapes, bounded span/error evidence, timings and environment facts. They
do not contain request/response bodies, headers, cookies, URL query values, SQL
bind parameters, screenshots, video or Playwright trace archives. They are still
local debugging artifacts; review them before sharing artifacts outside the
team. Never upload .stacknext/run.json or any file containing the collector
bearer token.
Concurrency and cleanup
- Use one capture per collector.
- Use one Playwright worker for captured runs.
- Give parallel CI jobs separate ports or separate ephemeral collectors.
- Always stop
stacknext devwith a signal and check for orphanednext devprocesses. - Set a job timeout.
- Keep
CI=trueso Playwright does not reuse a developer’s server.
What CI does not do
- It does not upload telemetry to StackNext or any hosted service.
- It does not run an LLM or ask an AI provider to classify a regression.
- It does not take screenshots, record video or read Playwright trace archives.
- It does not prove statistical significance or causality.
- It does not make an
inconclusivecomparison a pass. - It does not automatically delete old baselines or comparisons.