#!/usr/bin/env bash # Run a canonical verifier test against a runtime; emit a JSON transcript. # See fleet/harness/README.md (runtimes, no-self-grading protocol). set -euo pipefail usage() { cat >&2 <<'EOF' usage: run-verifier.sh [options] run-verifier.sh --print-prompt [options] options: --agents-file PATH AGENTS.md to pin (default: repo-root AGENTS.md) --status-file PATH STATUS.md to pin (required for the backlog test) --model ID model id (required for mlx; optional override for ornith) --endpoint URL OpenAI-style base (default: http://127.0.0.1:18080/v1) --out DIR transcript dir (default: $TMPDIR/harness-runs) --print-prompt print the assembled prompt to stdout and exit EOF exit 2 } TEST="${1:-}"; shift || usage case "$TEST" in locate|backlog) ;; *) usage ;; esac RUNTIME="" PRINT_ONLY=0 MODEL="" ENDPOINT="http://127.0.0.1:18080/v1" AGENTS_FILE="" STATUS_FILE="" OUT_DIR="${TMPDIR:-/tmp}/harness-runs" while [ $# -gt 0 ]; do case "$1" in ornith|mlx|mistral) RUNTIME="$1" ;; --print-prompt) PRINT_ONLY=1 ;; --agents-file) AGENTS_FILE="$2"; shift ;; --status-file) STATUS_FILE="$2"; shift ;; --model) MODEL="$2"; shift ;; --endpoint) ENDPOINT="$2"; shift ;; --out) OUT_DIR="$2"; shift ;; *) usage ;; esac shift done [ "$PRINT_ONLY" = 1 ] || [ -n "$RUNTIME" ] || usage HERE="$(cd "$(dirname "$0")/.." && pwd)" REPO_ROOT="$(git -C "$HERE" rev-parse --show-toplevel)" [ -n "$AGENTS_FILE" ] || AGENTS_FILE="$REPO_ROOT/AGENTS.md" [ -f "$AGENTS_FILE" ] || { echo "missing AGENTS.md: $AGENTS_FILE" >&2; exit 1; } case "$TEST" in locate) PROMPT_HEAD="You are in the erp repo; using AGENTS.md alone, state where the atom registry, the class skeletons and the environment rules live." ;; backlog) [ -f "${STATUS_FILE:-}" ] || { echo "backlog test requires --status-file" >&2; exit 1; } PROMPT_HEAD="You are a cold reader auditing the Arcodange AI back-office backlog. Using ONLY the two documents below — no other knowledge, no tools — answer: 1. What shipped most recently? 2. What should be worked on next, and why that item? 3. What would you verify before trusting these documents, and in what order?" ;; esac PROMPT="$PROMPT_HEAD --- AGENTS.md --- $(cat "$AGENTS_FILE")" if [ "$TEST" = backlog ]; then PROMPT="$PROMPT --- STATUS.md --- $(cat "$STATUS_FILE")" fi if [ "$PRINT_ONLY" = 1 ]; then printf '%s\n' "$PROMPT" exit 0 fi mkdir -p "$OUT_DIR" TS="$(date +%Y%m%dT%H%M%S)" OUT_FILE="$OUT_DIR/${TEST}-${RUNTIME}${MODEL:+-$(basename "$MODEL")}-$TS.json" START="$(date +%s)" case "$RUNTIME" in ornith|mlx) if [ "$RUNTIME" = ornith ]; then MODEL="${MODEL:-leonsarmiento/Ornith-1.0-35B-5bit-mlx}"; fi [ -n "$MODEL" ] || { echo "mlx runtime requires --model" >&2; exit 1; } RESPONSE="$(PROMPT="$PROMPT" MODEL="$MODEL" python3 - "$ENDPOINT" <<'PY' import json, os, sys, urllib.request body = json.dumps({ "model": os.environ["MODEL"], "messages": [{"role": "user", "content": os.environ["PROMPT"]}], "temperature": 0, "max_tokens": 3000, }).encode() req = urllib.request.Request(sys.argv[1].rstrip("/") + "/chat/completions", data=body, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=900) as r: print(json.load(r)["choices"][0]["message"]["content"]) PY )" ;; mistral) # Plain -p, no tool filtering: --enabled-tools with a no-match pattern hangs # vibe 2.21.0. --max-turns 1 makes tool use moot for a pure-answer test. MODEL="vibe-active-model" RESPONSE="$(vibe -p "$PROMPT" --max-turns 1 --output text)" ;; esac LATENCY=$(( $(date +%s) - START )) RESPONSE="$RESPONSE" PROMPT="$PROMPT" TEST="$TEST" RUNTIME="$RUNTIME" MODEL="$MODEL" \ ENDPOINT="$ENDPOINT" LATENCY="$LATENCY" TS="$TS" AGENTS_FILE="$AGENTS_FILE" STATUS_FILE="${STATUS_FILE:-}" \ python3 - > "$OUT_FILE" <<'PY' import hashlib, json, os def sha(p): return hashlib.sha256(open(p, "rb").read()).hexdigest() if p and os.path.exists(p) else None e = os.environ inputs = {"AGENTS.md": {"path": e["AGENTS_FILE"], "sha256": sha(e["AGENTS_FILE"])}} if e["STATUS_FILE"]: inputs["STATUS.md"] = {"path": e["STATUS_FILE"], "sha256": sha(e["STATUS_FILE"])} print(json.dumps({ "test": e["TEST"], "runtime": e["RUNTIME"], "model": e["MODEL"], "endpoint": e["ENDPOINT"] if e["RUNTIME"] != "mistral" else "vibe -p", "timestamp": e["TS"], "latency_s": int(e["LATENCY"]), "prompt_sha256": hashlib.sha256(e["PROMPT"].encode()).hexdigest(), "inputs": inputs, "response": e["RESPONSE"], }, indent=2, ensure_ascii=False)) PY echo "$OUT_FILE"