Skip to main content
StackNext regression checks fit into a normal CI job without a hosted StackNext service. The CI runner is the machine: the collector binds to loopback, the Playwright browser runs there, and run, baseline and comparison files stay in the workspace. The example uses GitHub Actions, but the same steps work in any runner that can run Node.js, Playwright and the app.
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.
Do not run captured Playwright tests in parallel. Correlation is verified per injected trace with one worker; a second worker can make the sample set incomplete.

GitHub Actions example

This example assumes the app directory is apps/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 with stacknext 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.
  1. Check out the known-good revision in a separate directory or worktree.
  2. Install, build and start that revision.
  3. Capture it and save a baseline.
  4. Check out the candidate revision in the primary workspace.
  5. Capture the candidate and compare.
Keep ports, app names and collector processes separate between the two revisions. Do not compare runs from two matrix entries.

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 source run-id and a token with actions: read:
Re-capture the baseline whenever Node.js, Next.js, Playwright, the database setup or the instrumentation flags change. A stale artifact is not a stable reference.

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:
Then:
If the runner is slow to start the dev server, raise the fixture timeout in the child environment:

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.
Always stop the process with SIGINT or SIGTERM in a cleanup step so it closes the collector and removes .stacknext/run.json:
Check ports 4400-4425 before the capture step if the runner reuses processes.

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 to inconclusive 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 dev with a signal and check for orphaned next dev processes.
  • Set a job timeout.
  • Keep CI=true so 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 inconclusive comparison a pass.
  • It does not automatically delete old baselines or comparisons.