// How-to for coding agents

How to gate a pull request with Kane CLI in CI

Short answer

On GitHub Actions, add the official kane-cli-browser-run action with your _test.md files and your TestMu AI credentials from repository secrets, then make the check required. Any failed test fails the check.

On any other CI, run kane-cli testrun run --tags smoke --parallel 4 --headless --agent with --username and --access-key from your secret store. A non-zero exit fails the job.

For Claude Code, Codex CLI, Gemini CLI, Cursor and GitHub Copilot · Checked against kane-cli 0.9.0 · Updated

When to make Kane CLI tests a merge gate

When committed _test.md files should block a merge: on every pull request, including the ones a coding agent opens, and before a release. An agent can run the same tests on its own machine before it pushes.

Steps to gate pull requests on GitHub Actions

  1. 1

    Commit each _test.md file with its output folder

    Commit each _test.md file with its output-<stem>/ folder. With it, CI replays the recorded steps with no LLM cost. Without it, Kane CLI authors the steps again on every CI run, which spends credits. Add tags: [smoke] to the frontmatter of each test the gate should run, because the commands below select with --tags smoke. See Save a verified run as a committed test.

  2. 2

    Add your TestMu AI credentials as repository secrets

    Create LT_USERNAME and LT_ACCESS_KEY in the repository's Actions secrets, using the username and access key from your TestMu AI account.

  3. 3

    Add a Kane CLI workflow file to .github/workflows

    Save this as .github/workflows/kane-smoke.yml. The action installs Node, Chrome and Kane CLI, signs in, runs the files as one testrun with --headless, uploads the evidence pack and posts the verdict on the pull request.

    yaml
    name: kane-smoke
    on: [pull_request]
    permissions:
      contents: read
      pull-requests: write
    jobs:
      smoke:
        runs-on: ubuntu-latest
        timeout-minutes: 30
        steps:
          - uses: actions/checkout@v4
          # build and start your app here if the tests open localhost
          - uses: LambdaTest/kane-cli/integrations/github-action/kane-cli-browser-run@main
            with:
              test-files: "tests/**/*_test.md"
              parallel: "4"
              extra-args: "--tags smoke"
              username: ${{ secrets.LT_USERNAME }}
              access-key: ${{ secrets.LT_ACCESS_KEY }}

    Pin a release tag instead of @main when you want a fixed version.

    pull-requests: write lets the action comment on the pull request. Pull requests from forks get no repository secrets and a read-only token, so the action cannot sign in there; run the check on branches in the repository.

    testrun has no timeout flag, so timeout-minutes bounds the suite. Tests that use {{name}} variables need one more step: see Pass test variables to Kane CLI tests in CI.

  4. 4

    Make the Kane CLI check required for merging

    A failing check blocks a merge only when it is required. In the repository settings, add a branch protection rule or ruleset for your default branch and mark the smoke job's check as required.

  5. 5

    Open a pull request and read the Kane CLI check

    The check fails when any test fails or the run exits non-zero. The pull request comment and the job summary report the verdict and link the evidence pack, which holds the screenshots and the console and network logs for the failing step.

Pass test variables to Kane CLI tests in CI

Tests that use {{name}} variables, such as a login test, need their values on the runner, and testrun has no --variables-file flag. Add a step before the Kane CLI action that writes them from repository secrets into .testmuai/variables/, which every run loads. Without it, the run types {{username}} literally and the test fails.

yaml
- name: Write Kane CLI test variables
  env:
    TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
    TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
  run: |
    mkdir -p .testmuai/variables
    jq -n --arg u "$TEST_USERNAME" --arg p "$TEST_PASSWORD" \
      '{username:{value:$u},password:{value:$p,secret:true}}' > .testmuai/variables/ci.json

Gate a pull request on any CI with one testrun command

Outside GitHub Actions, install Node 18 or later, Chrome and Kane CLI on the runner, start your app, then run the suite with credentials from your secret store:

bash
npm install -g @testmuai/kane-cli
kane-cli testrun run --tags smoke --parallel 4 --headless --agent \
  --username "$LT_USERNAME" --access-key "$LT_ACCESS_KEY"

--headless is required, because CI runners have no display. --agent forces the NDJSON stream. Most CI runners select it anyway because stdin is not a terminal, but on a runner that attaches a terminal, testrun without it opens an evidence table that never exits.

Archive .testmuai/evidence/*.evidence and each test's output-<stem>/Result.md as build artifacts so reviewers can open the result, and bound the suite with your CI job's timeout.

Kane CLI testrun exit codes a CI job can gate on

Kane CLI testrun exit codes a CI job can gate on
Exit codeMeaningWhat the exit code means for the merge
0All passedEvery test passed. The merge can go ahead.
1A test failedAt least one test failed or broke. Read its output-<stem>/Result.md or the evidence pack.
2Did not runA usage error, an invalid suite plan or a sign-in problem, so nothing ran. Check the credentials, and that every test belongs to the same organization and Test Manager project.
3CancelledThe suite was cancelled, for example with Ctrl-C. testrun has no timeout flag of its own, so a long suite is stopped by your CI job's timeout instead.

A local run's NDJSON stream ends with testrun_done, whose overall_status is passed, failed or cancelled; a --remote run adds remote_done after it. Gate on the exit code, because a run refused before it starts can end without either event.

Run the committed tests before a coding agent pushes

When a coding agent should check its own work before it opens a pull request, it runs the committed tests, not new objectives:

bash
kane-cli testrun run --tags smoke --headless --agent

Exit 0: continue with the commit or pull request. Exit 1: stop, read the failing test's output-<stem>/Result.md for the step, and report it to the user with the screenshot from the evidence pack in .testmuai/evidence/ instead of pushing. Exit 2: nothing ran; report it as "did not start" with the fix (sign-in, Chrome or an invalid suite plan) and do not push. Exit 3: the suite was cancelled; rerun it before you push.

Run a Kane CLI test suite on the TestMu AI cloud grid

To run the suite on the cloud grid rather than on the CI runner, install the plugin in the job (each CI runner starts clean) and preview the execution plan first. --dry-run resolves the execution plan without creating a job.

bash
kane-cli plugin install remote-execution
kane-cli testrun run --tags smoke --remote --dry-run --agent \
  --username "$LT_USERNAME" --access-key "$LT_ACCESS_KEY"
kane-cli testrun run --tags smoke --remote --parallel 4 --agent \
  --username "$LT_USERNAME" --access-key "$LT_ACCESS_KEY"

--remote needs a TestMu AI subscription that includes HyperExecute. The working directory is sent as the job and respects .gitignore, so keep the output-<stem>/ folders out of .gitignore. Use it with tests that open a deployed or preview URL, not an app that runs on the CI runner itself.

Kane CLI docs for CI gating

Questions about gating pull requests with Kane CLI

Is Chrome preinstalled on the CI runner?

Do not rely on it. The official action installs the stable Chrome release for you. In a hand-written workflow, add a step such as browser-actions/setup-chrome, or set KANE_CLI_CHROME_PATH to a Chrome the runner already has.

Should the CI job run kane-cli login?

It does not need to. Pass --username and --access-key on the testrun run command. kane-cli login with both flags also works (the official action uses it), but never run a bare kane-cli login in CI, because without flags it is interactive.

Do saved tests spend credits in CI?

Mostly when a test is authored or healed. Replayed steps have no LLM cost. A test without its committed output-<stem>/ recordings is authored during the run, and a heal re-authors the test from the failing step onward; both use credits. Commit the recordings so CI replays, and check the account with kane-cli balance.

Install Kane CLI to gate every pull request

Commit the tests your agent saved and add the workflow. Every pull request then replays them, and a required check stops the merge when one fails.

Start free