name: Verdikt gate
on:
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
verdikt-gate:
if: contains(github.event.pull_request.labels.*.name, 'verdikt:rc')
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Wait for Verdikt gate
env:
VERDIKT_API_URL: ${{ secrets.VERDIKT_API_URL }}
VERDIKT_API_KEY: ${{ secrets.VERDIKT_API_KEY }}
VERDIKT_WORKSPACE_ID: ${{ secrets.VERDIKT_WORKSPACE_ID }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
POLL_ATTEMPTS: "12"
POLL_INTERVAL_SEC: "10"
run: |
set -euo pipefail
URL="${VERDIKT_API_URL%/}/api/workspaces/${VERDIKT_WORKSPACE_ID}/gate"
QUERY="commit_sha=${COMMIT_SHA}&pr_number=${PR_NUMBER}&github_owner=${REPO_OWNER}&github_repo=${REPO_NAME}&mode=default"
echo "Polling ${URL}?${QUERY}"
LAST_RESP=""
for attempt in $(seq 1 "${POLL_ATTEMPTS}"); do
echo "--- Gate poll ${attempt}/${POLL_ATTEMPTS} ---"
LAST_RESP=$(curl -sS -f -H "Authorization: Bearer ${VERDIKT_API_KEY}" "${URL}?${QUERY}")
ACTION=$(echo "$LAST_RESP" | jq -r '.action // "unknown"')
STATUS=$(echo "$LAST_RESP" | jq -r '.status // "unknown"')
REASON=$(echo "$LAST_RESP" | jq -r '.gate.reason // ""')
echo "Verdikt: action=${ACTION} status=${STATUS} — ${REASON}"
if [ "$ACTION" = "merge" ]; then
echo "$LAST_RESP" | jq .
echo "Verdikt gate passed."
exit 0
fi
if [ "$ACTION" = "escalate" ]; then
echo "$LAST_RESP" | jq .
echo "$LAST_RESP" | jq -r '.blockers[]? | "- [\(.type)] \(.signal_id // .code // "release"): \(.message // .next_step // "")"' || true
NEXT_STEP=$(echo "$LAST_RESP" | jq -r '.next_step // empty')
[ -n "$NEXT_STEP" ] && echo "Next step: ${NEXT_STEP}"
echo "::error::Verdikt gate escalated — human override required."
exit 1
fi
if [ "$attempt" -lt "${POLL_ATTEMPTS}" ] && { [ "$ACTION" = "collecting" ] || [ "$ACTION" = "self_heal" ]; }; then
echo "Waiting for signals (action=${ACTION}) — sleeping ${POLL_INTERVAL_SEC}s..."
sleep "${POLL_INTERVAL_SEC}"
continue
fi
break
done
echo "$LAST_RESP" | jq .
echo "$LAST_RESP" | jq -r '.blockers[]? | "- [\(.type)] \(.signal_id // .code // "release"): \(.message // .next_step // "")"' || true
NEXT_STEP=$(echo "$LAST_RESP" | jq -r '.next_step // empty')
[ -n "$NEXT_STEP" ] && echo "Next step: ${NEXT_STEP}"
ACTION=$(echo "$LAST_RESP" | jq -r '.action // "unknown"')
STATUS=$(echo "$LAST_RESP" | jq -r '.status // "unknown"')
if [ "$STATUS" = "COLLECTING" ]; then
echo "::error::Verdikt gate timed out waiting for signals. Check integration wiring and SHA tagging on your eval runs."
elif [ "$STATUS" = "UNCERTIFIED" ]; then
echo "::error::Verdikt gate timed out — signals arrived but did not pass thresholds. Review blocking signals above."
else
echo "::error::Verdikt gate did not pass within timeout (last action=${ACTION}, status=${STATUS})."
fi
exit 1