concept · 04

Guards and evidence

Your agent ships before CI is green, deploys on a red build, approves its own PR. Declare 'only after the tests pass' once, as a guard the agent can't talk its way past.

Spell out what “ready” means, once

Take the move you don’t want your agent making on a whim — shipping. Here the ship transition carries a guard: a check the kernel runs before the move is allowed to fire. To ship, three things have to be true at once — the actor holds the prod.deploy permission, the test suite actually passed, and coverage is over 80%. Miss any one and the move is refused. You declare it here and only here.

checked:
  transitions:
    ship:                               # offered only in the 'checked' state
      target: shipped
      guards:
        - kind: allOf                   # every guard below must hold
          guards:
            - { kind: permission, permission: prod.deploy }
            - { kind: evidence, requires: [tests_passed] }
            - { kind: expr, expr: "$.context.coverage > 80" }

That one guard is composed of the kinds Praxec gives you: permission and role (who is acting), expr (a condition over the workflow’s shared context), and evidence (proof an earlier step really ran). Stack them with allOf, anyOf, and not. A move whose guard fails is refused with GUARD_REJECTED — distinct from INVALID_TRANSITION, which is what you get when you reach for a move that isn’t declared in this state at all.

Evidence: proof the runtime checks, that the agent can’t fake

Evidence is the piece worth slowing down for. Think of it like a required status check — except it’s the kernel that enforces it, not a setting your agent can route around. An evidence record is proof that earlier work really happened: the tests_passed record only exists because run_check actually ran your suite and it came back green. The agent can’t assert it; it has to have been earned. And when one approval isn’t enough, count:

request_publish:
  guards:
    - kind: evidence
      requires:
        - { name: approval, count: 2 }   # two approvals, not one

The move the agent should never make

Some moves shouldn’t be the agent’s at all — merging to main, approving its own pull request. Mark the transition actor: human and the agent is never even offered the link; if it reaches for the move anyway, the kernel rejects it with ACTOR_MISMATCH. This isn’t a line in the system prompt the model agrees to and then ignores two turns later — it’s a hard gate, and the only path forward belongs to a person.

← { "error": { "code": "ACTOR_MISMATCH",
      "message": "transition 'approve' requires actor: human" } }
← All concepts