Skip to main content
A verifier agent answers one question about every pull request: does the change actually do what it claims? It brings up your stack, runs the changed behavior with real inputs, observes the result, and posts one evidence-backed verdict as a PR comment. When new commits land, the same agent abandons the stale verification, verifies the new head, and updates its comment in place. When the PR closes, the agent is torn down. By the end of this guide you have:
  • A verifier agent persona in your tenant catalog that encodes the verification discipline.
  • A GitHub Actions workflow that uses the composite actions at prassoai/murmur-actions to spawn one persistent verifier when a PR opens, rerun it on every push, and kill it when the PR closes.

Why a verifier, not another test job

A test suite asserts intent; a verifier confirms reality. The persona’s core rules:
  • Exercise, don’t inspect. Reading the diff and concluding “looks right” is not verification. The verifier brings up the real system and drives the actual behavior a user or caller would see.
  • One verdict. Every run ends in exactly one of three verdicts: CONFIRMED CORRECT, CONFIRMED INCORRECT, or NOT LOCALLY TESTABLE, never “probably works.”
  • Evidence or it didn’t happen. A verdict carries the commands, inputs, and outputs that prove it, so anyone can re-run the steps and see the same thing.
  • One comment per PR. The verifier upserts a single marker-tagged comment instead of stacking a new comment on every push.

How it works

The workflow maps PR lifecycle events onto one long-lived agent with a stable slug, verify-<repo-id>-pr-<number>, built from the repository ID and the PR number: Cancelling a superseded verification lives in the agent (via preempt), not in CI job concurrency. The CI jobs stay thin calls that return in seconds; the long-running work is the agent’s. Because the agent persists, it keeps its context across commits and updates one verdict comment in place.

Prerequisites

  • A workspace whose repos include the repository you want verified: the agent checks out the PR head inside its VM. See the admin quickstart.
  • Permission to create a service profile and API key in your organization, and admin access to the GitHub repository (to add a secret and the workflow file).

Step 1: Author the verifier persona

The persona defines verdict rules, evidence rules, boundaries, and project-specific instructions for starting your system. Create it with murmur set agent-persona, piping Markdown with YAML frontmatter:
The persona has two parts. Everything outside “Your local environment” is the verification discipline. It is project-independent, and you can keep it as is. The “Your local environment” section is the one part that is yours: replace it wholesale with your project’s exact bring-up, seed, and teardown commands, the ports things listen on, and any harnesses the agent should prefer before declaring something untestable. The more concrete that section is, the fewer NOT LOCALLY TESTABLE verdicts you get.
The persona holds the reusable discipline; the per-PR facts (PR number, head SHA, which comment to upsert) arrive in the spawn prompt from the workflow in step 3. Editing the persona affects future spawns only; see agent persona lifecycle.
Confirm the persona with murmur get agent-persona verifier.

Step 2: Set up credentials

The workflow authenticates to Murmur with a service profile API key, so verifier runs belong to a bot identity rather than a person.
  1. Create a service profile (e.g. pr-verifier); see Create a service profile.
  2. Create an API key against that profile; see API keys. The key is shown once, in the form mur_….
  3. Store the key as a repository (or organization) secret named MURMUR_API_KEY:

Step 3: Add the workflow

Create .github/workflows/verify-pr.yml in the repository. Replace my-workspace with your workspace name, and adjust the numbered protocol in the prompt to your project:
The slug uses github.event.repository.id because the ID is unique and stable across repositories. The same file works unchanged in every repository. Because the ID is numeric, the prefixed value always satisfies the slug grammar ^[a-z][a-z0-9-]{0,62}$ (start with a lowercase letter; lowercase letters, digits, and hyphens only; max 63 characters). You can use the more readable github.event.repository.name only when the repository name already fits that grammar. Names such as socket.io, my_project, React, or 2048 produce an invalid slug and the spawn fails. Sanitize an invalid name or keep the ID.
The composite actions run the published Murmur CLI image on the runner: the same murmur spawn, murmur queue add --preempt, and murmur kill you can run by hand. Their full inputs and outputs are documented in the murmur-actions repository. Six details in the workflow are worth understanding before you adapt it:
  • The slug carries a repository discriminator. A slug identifies the agent within the workspace, not within the repository, and PR numbers repeat across repositories, with a bare verify-pr-42, two repositories in one workspace would resolve their PR #42 events to the same agent. The repository ID keeps the slugs distinct however many repositories share the workspace, and the purpose field keeps the human-readable repository name visible on the dashboard.
  • The same-repo gate. Every job requires github.event.pull_request.head.repo.full_name == github.repository. Fork PRs receive no repository secrets, so MURMUR_API_KEY is empty on a fork run. The gate keeps those events from running the jobs. Because a fork PR never spawns an agent, it also has nothing to re-verify or tear down.
  • needs: reverify with always(). The spawn job waits on reverify so the synchronize recovery path can read its delivered output. always() keeps spawn running on open/reopen, where the synchronize-only reverify job is skipped.
  • The concurrency group safely drops stale pending runs. GitHub queues at most one pending run per concurrency group. With cancel-in-progress: false, a running job always finishes. When a newer event arrives while another run is already pending, GitHub cancels the older pending run and gives the slot to the newer one. The jobs preserve the newest event. A re-verification is latest-wins by design. The deliver-or-spawn path recovers a pending spawn displaced by a re-verification. A reopen after a displaced teardown is covered by force-new: "true", and the kill tolerates an agent that is already gone.
  • The prompt is the single source of per-PR truth. PR-context values flow through the action’s with: inputs, never through a runner shell step, so a PR-author-controlled value like a branch name cannot inject script into the CI job. The prompt’s gh snippet runs later, inside the agent, not on the runner.
  • The verdict comment carries <!-- MURMUR_IGNORE -->. Murmur delivers PR comments to subscribed agents as events; a comment whose body contains this marker wakes no agent. Stamping it on the verdict keeps the verifier’s own machine-generated comment from arriving as a pr_comment follow-up to an agent working the PR. It is separate from <!-- pr-verifier -->, which the upsert step searches for.

Step 4: Pin the actions to a commit SHA

The spawn, reverify, and teardown jobs all carry MURMUR_API_KEY, so follow the standard practice for secret-bearing jobs: pin uses: to a full commit SHA rather than a moving tag or branch, so the action code your jobs run only changes when you change it. Look at the murmur-actions repository and pin its head commit, keeping a trailing comment naming the ref you resolved (# murmur-actions main) so the human-readable version stays visible. Resolve the head commit:
Use that SHA in all three uses: lines, and bump it deliberately when you want to roll the actions forward.

Open a pull request

Push the workflow, then open a PR against the repository. On open, the spawn job returns within seconds and the agent appears on your dashboard as verify-<repo-id>-pr-<number>. When it finishes, the PR carries one comment:
Push another commit and the comment updates in place with a verdict for the new head. Close the PR and the agent disappears from murmur ls.
Between commits the agent sleeps rather than terminating. You can talk to it like any other agent: murmur queue add to ask it to re-check something, murmur status to see what it’s doing.