For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /codifying-and-enforcing/verify-agent-changes-in-ci.md.

Verify agent changes in CI

Info

Takeaway Start a verifier as a warning, inspect the files it covers, and promote it to an error only after it produces reliable findings on real changes.

This guide creates a verifier for a repository invariant, connects it to tessl.json, and runs it against pull request changes. If you are deciding whether a verifier fits your standard, start with Verifiers overview.

Info

Prefer to start from generated candidates? Generate and review them in the web UI, then use Add to repository to have your agent propose the definitions and scope. Adding them doesn't enable CI. Your agent's tessl.json change already holds a group for each verifier, so skip the error-ux example in steps 1 and 2: set each new group's level to warn as step 2 explains, then calibrate it from step 3. See Generate and review verifiers in the web UI.

1. Write the verifier

Create verifiers/actionable-errors.json:

{
  "name": "User-facing errors include a next step",
  "instruction": "When code returns a user-facing error, it should tell the user what to do next.",
  "relevant_when": "TypeScript code that constructs a UserFacingError or prints an error for a CLI command.",
  "context": "CLI errors should be actionable for users running commands in a terminal.",
  "scope": "file",
  "scope_reason": "Each command file contains the error and its recovery guidance.",
  "checklist": [
    {
      "name": "actionable-error",
      "rule": "Each user-facing error includes the command to run next, the flag to change, or the file to inspect."
    }
  ]
}

Keep relevant_when specific. It should name the observable trait that makes a target applicable, rather than a broad category such as "TypeScript file."

Validate the file before running it:

tessl change verify lint verifiers/actionable-errors.json

2. Configure its scope as a warning

Add a group to the verify block in tessl.json:

{
  "name": "engteam/my-project",
  "verify": {
    "groups": {
      "error-ux": {
        "path": "verifiers/actionable-errors.json",
        "level": "warn",
        "include": ["src/**/*.ts"],
        "exclude": ["**/*.test.ts"]
      }
    }
  }
}

Severity belongs in tessl.json, not in the verifier file. Starting at warn reports findings without blocking the pull request.

3. Inspect the matched files

Preview a full-repository run before spending judge calls:

tessl change verify --dry-run --all --show-files

Check that the output contains the files you intend to cover and excludes unrelated files. Adjust the group's include and exclude globs or the verifier's relevant_when if the scope is too broad or narrow.

Then judge a small sample:

tessl change verify --all --sample 5

This sends the selected source content to Tessl's LLM judge. Limit the verifier's scope to content your organization is authorized to send, and exclude credentials, personal data, and other sensitive material.

Review each result against the invariant. Refine the verifier until the same evidence produces consistent pass or fail decisions.

4. Run it on pull requests

Run verifiers on pull requests with the Tessl Reviewer GitHub App, or with a workflow you own. The App needs no workflow and no stored token.

With the Tessl Reviewer GitHub App

  1. Sign in to tessl.io, select your organization, and open Reviewer in the sidebar.
  2. Open the Verify PRs tab. If the App is not installed yet, select Connect GitHub and install it as described in Set up Tessl Code Review.
  3. Select Turn on next to the repository, switch on Turn on for this repository, and select Save settings.

Turning Verify PRs on and changing its settings takes an organization admin.

Verify PRs runs when a pull request opens or gets new commits, and when someone comments @tessl-verify. It checks the pull request against its base branch the same way as the workflow below run with --recursive, so it covers every linked manifest in the repository, and reports the result as a Tessl Verify GitHub Check. A finding from a warn verifier shows as a warning and the Check still passes. A finding from an error verifier fails the Check. If tessl.json declares no verifiers, the Check is skipped.

The repository's settings also set:

  • Run on draft pull requests: when off, a draft gets a skipped Check until it is marked ready for review or someone comments @tessl-verify.
  • Judge calls per run: the most judge calls one run makes. A run that needs more stops without judging, and its Check says it did not finish.
  • Time limit, seconds: how long a run may spend judging.

The Verify PRs tab shows each repository's last result. When a repository has no verifiers yet, its settings offer Write verifiers with an agent: a prompt that has a coding agent propose verifiers for the repository and open a pull request.

In your own GitHub workflow

CI needs a Tessl API key in the TESSL_TOKEN environment variable. For key creation and storage guidance, see Authenticating in CI. A pull request can change its workflow, verifier, and manifest configuration, so its branch location does not establish trust. Use a pull_request workflow, not pull_request_target, and protect the credentialed job with a GitHub environment that requires approval from reviewers who are authorized to approve the configuration and source changes.

The following job and steps assume the workflow runs on pull_request, has checked out the repository, and installed Tessl with tesslio/setup-tessl@v2. The job skips an external head repository by comparing repository identities, so it still runs for same-repository pull requests when the repository itself is a fork. Create the tessl-verify environment in GitHub with required reviewers, store TESSL_TOKEN as an environment secret, and do not approve it until the pull request's workflow, verifier, and manifest changes have been reviewed:

verify:
  runs-on: ubuntu-latest
  if: github.event.pull_request.head.repo.full_name == github.repository
  environment: tessl-verify
  env:
    TESSL_TOKEN: ${{ secrets.TESSL_TOKEN }}
  steps:
    - name: Fetch base branch
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: git fetch --no-tags origin "$BASE_REF:refs/remotes/origin/$BASE_REF"

    - name: Inspect verifier scope
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: tessl change verify --base "origin/$BASE_REF" --dry-run --show-files --max-calls 200

    - name: Verify changed files
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: tessl change verify --base "origin/$BASE_REF" --github --max-calls 200

--base "origin/$BASE_REF" compares against the pull request's actual target branch, which the fetch step records as a remote-tracking ref. For source-only changes, the command verifies the pull request's changed files. When the pull request changes a verifier JSON file or the verify configuration, the affected verifier runs across its full configured scope. --github emits annotations for findings. A warn finding is advisory, while an error finding makes the command exit non-zero.

--max-calls 200 is the gate's call budget. The command stops before judging if its planned scope exceeds that limit; narrow the group's scope or choose and document a larger limit before changing the workflow.

If you run the command from a monorepo root with linked child manifests, add --recursive to each invocation.

5. Promote the verifier to a blocking check

Leave the verifier at warn while it runs on real pull requests. Track false positives, missed applicable files, and unclear findings. When the verifier is reliable, change the group level:

{
  "verify": {
    "groups": {
      "error-ux": {
        "path": "verifiers/actionable-errors.json",
        "level": "error",
        "include": ["src/**/*.ts"],
        "exclude": ["**/*.test.ts"]
      }
    }
  }
}

The Tessl Verify Check, or your own workflow's command, now fails on pull requests with failing findings. You do not need to change any settings or the workflow, or add --fail-on-warn. To stop those pull requests from merging, make the check required in the branch's protection rules.

Next