Ways to run
Gate a release in CI
Run a check from a CI job and ship only when the decision is verified.
- Outcome
- A release goes out only when the claim held on the live app.
- Does not do
- A CI job cannot approve a plan. The first check of a claim waits until a person approves it at the link.
Gate on the decision, never the run state. A finished run is not a pass. In CI the exit code is what an if-statement reads.
With the CLI
# In CI: gate the deploy on the DECISION, not on the command finishing. curl -fsS https://vraelis.com/install | sh vraelis verify --url "$PREVIEW_URL" --claim "$CLAIM" --wait --json > result.json # The approval link is printed to the log; the job waits until a person approves. # exit 0 verified exit 1 failed exit 2 blocked, or could not run # # Blocked is not a pass. A run that merely finished is not a pass. # Only exit 0 should ship. # On a failure, hand the repair prompt straight to a coding agent: vraelis result "$(jq -r .verification_id result.json)" --repair-prompt | claude -p
| Exit code | What it means |
|---|---|
0 | Verified. The claim held on the live app, with evidence. |
1 | Failed. The claim did not hold; a repair prompt is attached. |
2 | Blocked, or the tool could not run at all. |
The job needs VRAELIS_API_KEY in its environment, from a key with launch access, and Node 18 or newer. The CLI does not open a browser in CI: the approval link goes to the log. Create an API key
The first check of a claim
A CI job has no person at the terminal, and a key cannot approve a plan, so the first check of a claim waits on the link until someone approves it or --timeout runs out. Re-checks of that approved plan need no new approval within 24 hours of the approval, up to 10 times, on the same address. A new preview URL is a different address, so its first check waits for a person again. The gate below splits the two cases instead.
A gate that calls the API
The CLI collapses “blocked” and “could not run” into 2, because a gate should treat “I could not check” the same as “no verdict.” A hand-rolled gate that polls the API can split those out, exiting 3 when no decision is reached in the window.
// gate.mjs: ship only on "verified". The exit code gates the deploy. // 0 verified 1 failed 2 blocked 3 no decision reached 4 the plan still needs a person to approve it import { randomUUID } from "node:crypto"; const API = "https://vraelis.com/api/v1/verifications"; const headers = { "content-type": "application/json", "x-api-key": process.env.VRAELIS_API_KEY, "idempotency-key": randomUUID(), }; const request = { deployment_url: process.env.PREVIEW_URL, claim: process.env.VRAELIS_CLAIM }; const post = (body) => fetch(API, { method: "POST", headers, body: JSON.stringify(body) }); // A submit with no reviewed_plan_id returns review_required, not a run: no verification_id, nothing charged. // A key cannot approve the plan, so the job hands the link to a person and stops. const planId = process.env.VRAELIS_REVIEWED_PLAN_ID; if (!planId) { const plan = await (await post(request)).json(); console.error("A person approves this plan first: " + plan.approve_url); process.exit(4); } // The approved plan is what starts a run, and this response is the first to carry a verification_id. const { verification_id } = await (await post({ ...request, reviewed_plan_id: planId })).json(); // While running, decision is absent; keep polling until it lands. let decision = null, out; for (let i = 0; i < 120 && decision === null; i++) { await new Promise((r) => setTimeout(r, 5000)); out = await (await fetch(API + "/" + verification_id, { headers })).json(); decision = out.decision ?? null; } // Gate on the decision, never the run state. A finished run is not a pass. switch (decision) { case "verified": process.exit(0); case "failed": process.exit(1); case "blocked": process.exit(2); default: process.exit(3); // no decision within the polling window }
| Exit code | What it means |
|---|---|
0 | verified |
1 | failed |
2 | blocked |
3 | no decision reached |
4 | the plan still needs a person to approve it |
| Variable | What it holds |
|---|---|
VRAELIS_API_KEY | An API key with launch access. |
PREVIEW_URL | The deployment to check. |
VRAELIS_CLAIM | The sentence that has to hold. |
VRAELIS_REVIEWED_PLAN_ID | The approved plan's id. Unset on the first run, so the gate prints the approval link and exits 4. |