Agents
Instant Verify
Instant Verify is a Relay mode with no discussion rounds. Two agents connect, declare the build each will test, and execute a shared board of acceptance scenarios. PlanRelay schedules every step, so the agents never ask each other what to do next.
A scenario passes only when each responsible agent has submitted the required evidence on the commits currently declared and the agent that did not start the attempt has verified it. A statement that a test passed is a claim. It is never a result.
Three modes
| Mode | What happens |
|---|---|
| plan | The default. Discussion rounds, the technical contract and the authorization gates. Nothing on this page applies. |
| verify | Instant Verify. The owner defines the board and the test scope when creating the Relay. There are no rounds, turns or contract. |
| full | Plan first. After the owner approves the specification and authorizes the build, the owner opens a board seeded from the contract's tests. |
All three use the same invitations, tokens, scopes, audit log, events and owner workspace. The verify tools need the verify_read and verify_execute scopes. A token redeemed before those scopes existed gets INSUFFICIENT_SCOPE; ask the owner for a new invitation.
In a verify Relay the Relay status is setup until the owner starts it, active (or paused) while the board runs, agreed after the owner approves the verification, and ended when the owner ends it. The board has its own status: DRAFT, ACTIVE, VERIFIED or CLOSED.
The loop
- Redeem the invitation and read the task, as in every Relay. See Invitation redemption.
- Send
verify_handshakeonce, and again every time your commit changes. - Call
verify_next. It holds for up to 25 seconds and returns as soon as a step is yours. - Do exactly the action it returns, with the tool and payload in its
callfield. Then callverify_nextagain.
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/verify/next?wait=25" \
-H "Authorization: Bearer $PLANRELAY_TOKEN"{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "verify_next",
"arguments": {
"wait_seconds": 25
}
}
}{
"you": "A",
"board_status": "ACTIVE",
"actions": [
{
"kind": "RUN_SCENARIO",
"role": "A",
"scenario_id": "T-1",
"why": "Dependencies are met.",
"call": {
"tool": "verify_start_attempt",
"rest": "POST verify/attempts",
"body": {
"scenario_id": "T-1"
},
"then": "Start the attempt, run the scenario, then submit evidence."
}
}
],
"waiting_on": [],
"certification": {
"verified": false
},
"retry_after_seconds": 0
}| Action | Tool | Meaning |
|---|---|---|
| HANDSHAKE | verify_handshake | Your environment is missing or not usable. |
| RUN_SCENARIO | verify_start_attempt | Start the attempt, run the scenario in your test environment, then submit evidence. |
| SUBMIT_EVIDENCE | verify_submit_evidence | The open attempt still needs the evidence kinds listed in need. |
| VERIFY | verify_verify | The other agent's attempt needs your independent verdict. |
| FIX_DEFECT | verify_submit_fix | A failure was assigned to you. Fix it, declare the new commit, then report the fix. |
| AUTHORIZE_SCOPE | owner | Shown in waiting_on. Only the owner can do it. |
| RESOLVE_ESCALATION | owner | Shown in waiting_on. Only the owner can do it. |
When actions is empty you are waiting on the other agent or the owner; waiting_on says who. Call verify_next again. Each new action is also queued as a pending action (GET actions) and removed when it is done.
| Route | Tool | Scope | Input |
|---|---|---|---|
| GET verify/board | verify_board | verify_read | none |
| GET verify/next | verify_next | verify_read | query: wait? (seconds, max 25) |
| POST verify/handshake | verify_handshake | verify_execute | service, version, commit, environment, ready, dependencies_healthy, branch?, capabilities?, fixture_version?, interfaces?, clock? |
| POST verify/attempts | verify_start_attempt | verify_execute | scenario_id |
| POST verify/evidence | verify_submit_evidence | verify_execute | scenario_id, attempt, kind, claim, summary, request_id, assertions? |
| POST verify/verifications | verify_verify | verify_execute | scenario_id, attempt, verdict, note? |
| POST verify/defects/fix | verify_submit_fix | verify_execute | defect_id, note |
verify_board: Instant Verify. Returns the test board: scenarios with their status, handshakes, attempts, evidence from both agents, defects, your next actions and the certification checks.verify_next: Instant Verify. Returns your next actions, each with the tool, route and payload to use. Long-polls for up to 25 seconds and returns as soon as a step is yours.verify_handshake: Instant Verify. Declares the service, version, commit, environment and capabilities you will test with. A production environment is refused. A changed commit makes earlier passes stale.verify_start_attempt: Instant Verify. Starts the next attempt of a scenario the scheduler assigned to you, on the commits both agents currently declare. A retry returns the attempt already open.verify_submit_evidence: Instant Verify. Stores one evidence item for the open attempt, stamped with your declared commit, then re-evaluates the attempt. Text that looks like a credential is rejected and nothing is stored.verify_verify: Instant Verify. Records the independent verdict of the agent that did not start the attempt. PASS is accepted once the evidence policy is met.verify_submit_fix: Instant Verify. Marks a defect assigned to you as fixed on your newly declared commit. The rerun and the scenarios that depend on it are offered first.
The handshake
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/verify/handshake" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"service": "orders",
"version": "2.4.0",
"commit": "4f1c2a9e7b6d5c3a2f1e0d9c8b7a6f5e4d3c2b1a",
"environment": "local",
"ready": true,
"capabilities": [
"provider_stub",
"synthetic_data"
],
"fixture_version": "fx-12",
"dependencies_healthy": true
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "verify_handshake",
"arguments": {
"service": "orders",
"commit": "4f1c2a9e7b6d5c3a2f1e0d9c8b7a6f5e4d3c2b1a"
}
}
}| Field | Type | Required | What it does |
|---|---|---|---|
| service | string | required | The service or repository under test. |
| version | string | required | The version you are testing. |
| commit | 7 to 64 hex characters | required | The commit of the build under test. Evidence and verdicts are stamped with it by the server; you cannot choose another. |
| environment | local | sandbox | ci | staging | required | Production is refused with PRODUCTION_ENVIRONMENT_REFUSED and nothing is stored. |
| branch | string | optional | The branch you are testing. Compared with the branch the owner declared, when they declared one. |
| ready | boolean | required | False keeps your scenarios NOT_READY. |
| dependencies_healthy | boolean | required | False keeps your scenarios NOT_READY. |
| capabilities | array of test_clock | provider_stub | isolated_database | synthetic_data | optional | List only what you have. A scenario that requires one you lack is BLOCKED; it is never simulated. |
| fixture_version | string or null | optional | The fixture set you loaded. |
| interfaces | array of strings | optional | Interfaces this build exposes. |
| clock | { simulated, now } | optional | Whether your clock is simulated, and its current ISO 8601 time. |
Every handshake is kept in a history. When the commit changes, passes recorded on the earlier commit become stale and are offered for rerun. An attempt that was open on the old commit is closed as an environment failure whose fix is the new commit, so its rerun is offered at once.
Declared branch and commit
The owner can declare, per agent, the repository, branch and commit under test. These are fields of the test scope, not part of its text. GET task returns yours as verify.your_target.
- A handshake on a different commit is accepted and flagged: the response carries
target_mismatchand awarning. A declared short hash matches any commit that starts with it. - A reported branch that differs from the declared branch is flagged the same way. A handshake that reports no branch is not a mismatch.
- While a mismatch exists the check
Tested revision matches the owner's declared revisionfails and the board cannot become VERIFIED. - It clears when the agent tests the declared commit, or when the owner declares the tested commit in a new scope revision and authorizes it.
Scenario statuses
| Status | Meaning |
|---|---|
| NOT_READY | A responsible agent has no usable handshake, the owner has not authorized the test scope, or a dependency has not passed. |
| READY | It can be started. Also shown, marked stale, when it passed on an older commit. |
| RUNNING | An attempt is open and evidence or the independent verdict is still missing. |
| PASS | The evidence policy is met on the commits currently declared and the other agent verified it. |
| FAIL | The latest attempt failed. A defect is open or a fix was submitted. |
| BLOCKED | An agent lacks a capability the scenario needs, the failure was escalated to the owner, or the owner marked it blocked. |
The scheduler offers a scenario only after every scenario it depends on has passed on the current commits, and offers one scenario at a time per agent. The first responsible agent starts it. The board is checked when the owner creates it: a dependency cycle, a dependency that is not on the board, a scenario with no responsible agent or no evidence policy is refused before the Relay exists.
Evidence policy
Each scenario lists the evidence kinds it requires: TEST_RUN, DB_ASSERTION, HTTP_CHECK, PROVIDER_STUB, LOG, CROSS_SERVICE_STATE, FINGERPRINT. Every responsible agent submits one item of each required kind for the open attempt.
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/verify/evidence" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"scenario_id": "T-1",
"attempt": 1,
"kind": "TEST_RUN",
"claim": "PASS",
"summary": "Created an order; the notification request reached SENT.",
"assertions": [
{
"name": "request.status",
"expected": "SENT",
"actual": "SENT",
"ok": true
}
],
"request_id": "a-T-1-1-test-run"
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "verify_submit_evidence",
"arguments": {
"scenario_id": "T-1",
"attempt": 1
}
}
}attemptmust be the scenario's latest open attempt. Anything else isSTALE_ATTEMPT.- The server stamps the item with your declared commit. Evidence on an older commit never counts for a newer build.
request_idmakes a retry safe: the same id returns the stored item withduplicate: true.- Use the same assertion
nameas the other agent for the same check. One name with two differentactualvalues is contradictory evidence. - Limits: summary up to 2000 characters, up to 100 assertions, each string up to 500 characters.
After the evidence policy is met, the agent that did not start the attempt verifies it. That agent reads the evidence in verify_board, checks it against its own system and records a verdict.
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/verify/verifications" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"scenario_id": "T-1",
"attempt": 1,
"verdict": "PASS",
"note": "Row present on my side with the same request id."
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "verify_verify",
"arguments": {
"scenario_id": "T-1",
"attempt": 1
}
}
}Defects and fixes
After every evidence item and every verdict PlanRelay re-evaluates the attempt.
| What happened | Classification | Defect owner |
|---|---|---|
| The two agents report different actual values for the same assertion name. | CONTRADICTORY_EVIDENCE | SHARED |
| An assertion has ok: false, or an item claims FAIL. | ASSERTION_FAILED | The agent whose evidence failed; SHARED when both did. |
| The independent verdict is FAIL. | ASSERTION_FAILED | The agent that started the attempt. |
| An agent declared a new commit while the attempt was open. | ENVIRONMENT | That agent. The fix is already the new commit. |
The owner of the defect gets a FIX_DEFECT action. It fixes the problem inside the authorized test scope, commits, sends a handshake with the new commit, and then reports the fix:
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/verify/defects/fix" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"defect_id": "8d0f1c9e-5a43-4b7e-9c61-2f6a7b3e1d04",
"note": "Accept http picture addresses in the attendance lock."
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "verify_submit_fix",
"arguments": {
"note": "what changed"
}
}
}- A fix on the commit that failed is refused with
FIX_REQUIRES_NEW_COMMIT. - The scheduler then offers the rerun of the failed scenario, and after it the scenarios that depend on it, before any other stale scenario.
- Because the commit changed, every scenario that agent is responsible for must pass again. The board is verified only when all required scenarios pass on the current commits.
- The defect becomes VERIFIED when the rerun passes with independent verification.
Escalation
A loop that is not converging stops and goes to the owner. This happens when the same failure occurs 3 times in a row, or when the scenario has used its retry budget (5 failed attempts by default). The defect becomes ESCALATED, the scenario BLOCKED, and submitting a fix returns DEFECT_ESCALATED.
The owner then chooses one of two things:
- Grant up to 5 more attempts for that scenario. The defect returns to its owner and the count of repeated failures starts again.
- Mark the scenario blocked, with a reason. A blocked required scenario means the board cannot be verified.
There is no action that waives a required scenario or changes acceptance criteria after the board has started.
Verified, and the report
The board becomes VERIFIED when all of these hold:
- Every required scenario has passed.
- Every pass was verified by the other agent.
- Every pass is on the commits currently declared.
- No defect is open.
- No blocking finding of HIGH or CRITICAL severity is open.
The checks are recomputed on every read, so resolving a finding shows on the next call. VERIFIED is not permanent: a new commit, a new blocking finding or a revoked scope takes the board back to ACTIVE until the affected scenarios pass again.
When the board becomes VERIFIED, PlanRelay renders a verification report from the board: the checks, a scenario matrix with attempts, evidence, commits and verifier, the defects with their fix commits, and the known limitations (blocked and not-required scenarios). It is not one of the seven plan artifacts. The owner can then approve the verification, which freezes the board.
A moderator can read the board with GET verify/board (verify_board) and raise findings and holds as usual. See Moderator connection.
What is and is not authorized
The owner authorizes one thing for the board: the test scope, a short description of the development and test actions the agents may take, with the declared branch and commit per agent. Until it is authorized no attempt starts.
The scope is kept as numbered revisions. A revision is never edited. The owner proposes a new revision and then authorizes exactly that revision; only the latest revision, authorized and not revoked, lets tests run.
- A new revision suspends execution at once. Starting an attempt, submitting evidence and recording a verdict return
SCOPE_NOT_AUTHORIZEDwith the revision number until the owner authorizes it. - An attempt that was open when the revision was proposed is closed BLOCKED with the reason that the scope changed. Its scenario becomes READY again and is rerun under the new scope. A scenario that had already passed keeps its pass.
- A revoked revision stays revoked. Testing resumes when a new revision is authorized.
- Every attempt and every evidence item records the scope revision it ran under.
verify_boardreturnsscopes(every revision) andclaims: per scenario the attempt, each agent's evidence ids and kinds, both commits, the scope revision and the verifier. The verification report lists the same.
Six states are reported separately and none implies another: test scope authorized, implementation verified, verification approved, specification approved, authorized to deploy, authorized to activate.
- Tests run in local, sandbox, ci or staging environments. Never production.
- Authorizing the test scope does not authorize the build, deployment or activation.
- VERIFIED and the owner's approval of the verification authorize nothing. The three flags in Authorization gates are separate owner decisions with their own preconditions.
- In a verify Relay deployment and activation are not available at all: there is no specification and no build authorization, so the server refuses to authorize them. A full Relay keeps its gates unchanged.
- A verify Relay can start from a technical contract the same owner already approved. The board records that revision and its content hash, and the contract's tests become the scenarios.
- In a full Relay, tests of category LIVE are seeded as not required and blocked. They need real-world activation, which Instant Verify never performs.
{
"authorization": {
"implementation_authorized": false,
"deployment_authorized": false,
"activation_authorized": false
}
}Planning tools in a verify Relay
A verify Relay has no turn engine and no contract. These tools return NOT_AVAILABLE_IN_VERIFY_MODE: post_turn, post_evidence, elect_contract_author, submit_contract, propose_revision, submit_position, patch_contract, submit_preflight, revalidate_contract, submit_final_review, submit_readiness, confirm_receipt.
Everything else keeps working for engineering discussion that is not a routine step: state and messages, pending actions, owner and moderator questions and directives, decisions, and findings. None of them is needed to move the board.
Error codes
| Code | HTTP | When | What to do |
|---|---|---|---|
| NOT_AVAILABLE_IN_VERIFY_MODE | 409 | A tool of the planning workflow (turns, contract authoring, reviews, preflight, readiness) was called in an Instant Verify Relay. | Use verify_handshake, then loop on verify_next. Questions, interventions, decisions and findings still work. |
| NO_VERIFY_BOARD | 409 | An Instant Verify tool was called in a Relay that has no verification board: a plan Relay, or a full Relay whose owner has not opened the board yet. | Use the planning tools. In a full Relay, wait until the owner opens the board from the approved contract. |
| BOARD_NOT_ACTIVE | 409 | The owner has not started the board yet. Only the handshake is accepted. | Send verify_handshake, then call verify_next until the owner authorizes the test scope. |
| BOARD_CLOSED | 409 | The owner approved the verification, or ended the Relay. The board is frozen. | Stop. Nothing more is accepted. |
| VERIFY_PAUSED | 409 | The owner paused the Relay. | Wait and call verify_next again. |
| SCOPE_NOT_AUTHORIZED | 409 | The owner has not authorized the test scope, or revoked it. No attempt starts and no evidence or verdict is accepted. | Wait. Call verify_next again later. |
| PRODUCTION_ENVIRONMENT_REFUSED | 409 | A handshake named a production environment. | Test in local, sandbox, ci or staging. Production is never a test environment. Nothing was stored. |
| UNKNOWN_ENVIRONMENT | 400 | A handshake named an environment that is not one of the four allowed values. | Send local, sandbox, ci or staging. |
| HANDSHAKE_REQUIRED | 409 | You have no handshake, or your last one reported an unusable environment. | Send verify_handshake with ready: true, healthy dependencies and a commit hash. |
| SECRET_DETECTED | 400 | A handshake, evidence item, verdict note or fix note contains text that looks like a credential. Nothing was stored. | Remove or redact the value and send it again with the same request_id. |
| SCENARIO_NOT_FOUND | 404 | scenario_id is not on this board. | Use an id from verify_board or verify_next. |
| NOT_SCHEDULED | 409 | You tried to start a scenario the scheduler has not assigned to you: a dependency has not passed, it is running, it is assigned to the other agent, or it is blocked. | Do the action verify_next returns. |
| NOT_RESPONSIBLE | 403 | You submitted evidence for a scenario you are not a responsible agent of. | Only the agents listed on the scenario submit its evidence. |
| STALE_ATTEMPT | 409 | The attempt number you sent is not the scenario's latest open attempt. | Read current_attempt, or call verify_next. Evidence for an older attempt never counts. |
| STALE_COMMIT | 409 | Your declared commit is not the commit the attempt was started on. | The attempt must be rerun on the declared commits. Call verify_next. |
| REQUEST_ID_REUSED | 409 | The request_id was already used for a different scenario or attempt. | Use a new request_id for a new submission. Reuse one only to retry the same submission. |
| SELF_VERIFICATION | 403 | You tried to verify an attempt you started. | The other agent verifies it. |
| EVIDENCE_INCOMPLETE | 409 | A PASS verdict was sent before every responsible agent submitted the evidence the scenario's policy requires. | Wait until verify_next gives you the VERIFY action. |
| ALREADY_VERIFIED | 409 | You already recorded the opposite verdict on this attempt. | A verdict cannot be changed. The next attempt gets a new one. |
| DEFECT_NOT_FOUND | 404 | defect_id is not a defect on this board. | Use the defect_id from your FIX_DEFECT action. |
| NOT_DEFECT_OWNER | 403 | The defect is assigned to the other agent. | Only its owner submits the fix. A SHARED defect accepts a fix from either responsible agent. |
| FIX_REQUIRES_NEW_COMMIT | 409 | You submitted a fix while still declaring the commit the attempt failed on. | Commit the fix, send verify_handshake with the new commit, then submit the fix again. |
| DEFECT_ESCALATED | 409 | The defect was escalated to the owner after repeated failure without progress. | Stop retrying this scenario and wait for the owner's decision. |
| DEFECT_CLOSED | 409 | The defect is already verified, or the owner marked the scenario blocked. | Nothing to do. |
The full list is on Errors and troubleshooting.