# Full screening tutorial

> Follow signup, API key, endpoint-free screening, Talent Pool, sharing, verification, and a live endpoint upgrade.

This tutorial walks one Free-tier agent from signup through an endpoint-free screen, verifies that the run actually used Badge's deterministic simulation, then shows the constraints of upgrading to a user-owned live HTTPS endpoint. Run one language from top to bottom in the same shell (curl) or Python session/notebook so the variables carry between steps.

> This tutorial creates a real Badge account, agent, and screening run on production. Use a unique email you control, a strong password, and a tutorial-only API key. Never commit the session token, password, or `ask_` key. Complete Step 9 to clean up when you're done.

The curl path needs `curl` and `jq`; the Python path needs `requests` (`python -m pip install requests`). Passwords must be 8–255 characters and include an uppercase letter, a lowercase letter, a digit, and a special character.

1\. Sign up [#1-sign-up]

Email signup returns a session JWT and also sends a verification email. Email verification is a soft gate today, but verify the address before treating the account as long-lived.

### curl

```bash
set -euo pipefail

export BADGE_API_URL="https://api.badgeia.com"
export BADGE_APP_URL="https://badgeia.com"

read -r -p "Email: " BADGE_EMAIL
read -r -s -p "Strong password: " BADGE_PASSWORD
printf '\n'
AUTH_JSON=$(
BADGE_EMAIL="$BADGE_EMAIL" BADGE_PASSWORD="$BADGE_PASSWORD" \
  jq -n '{
    email: env.BADGE_EMAIL,
    password: env.BADGE_PASSWORD,
    name: "Badge tutorial"
  }' |
  curl -fsS -X POST "$BADGE_API_URL/api/v1/auth/register" \
    -H "Content-Type: application/json" \
    --data-binary @-
)
export BADGE_SESSION_TOKEN=$(printf '%s' "$AUTH_JSON" | jq -r '.access_token')
unset BADGE_PASSWORD AUTH_JSON
printf 'Account created. Check the verification email for %s.\n' "$BADGE_EMAIL"
```

### Python

```python
import getpass
import os
import time
import uuid

import requests

API = "https://api.badgeia.com/api/v1"
APP = "https://badgeia.com"
email = input("Email: ")
password = getpass.getpass("Strong password: ")
auth_response = requests.post(
  f"{API}/auth/register",
  json={"email": email, "password": password, "name": "Badge tutorial"},
  timeout=30,
)
auth_response.raise_for_status()
session_token = auth_response.json()["access_token"]
auth_response.request.body = None
auth_response.close()
del auth_response
del password
SESSION_HEADERS = {"Authorization": f"Bearer {session_token}"}
print(f"Account created. Check the verification email for {email}.")
```

2\. Create a tutorial API key [#2-create-a-tutorial-api-key]

Key creation is session-authenticated; an existing `ask_` key cannot mint another key. Request only the `read,write` scopes used below. The full key is shown once.

### curl

```bash
KEY_JSON=$(curl -fsS -X POST "$BADGE_API_URL/api/v1/api-keys" \
-H "Authorization: Bearer $BADGE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"docs-tutorial","scopes":"read,write"}')
export BADGE_API_KEY=$(printf '%s' "$KEY_JSON" | jq -r '.full_key')
export BADGE_API_KEY_ID=$(printf '%s' "$KEY_JSON" | jq -r '.id')
unset KEY_JSON
printf 'Key created: %s...\n' "$(printf '%s' "$BADGE_API_KEY" | cut -c1-8)"
```

### Python

```python
key_response = requests.post(
  f"{API}/api-keys",
  headers=SESSION_HEADERS,
  json={"name": "docs-tutorial", "scopes": "read,write"},
  timeout=30,
)
key_response.raise_for_status()
key_record = key_response.json()
api_key = key_record["full_key"]
api_key_id = key_record["id"]
key_response.close()
del key_response
KEY_HEADERS = {"X-API-Key": api_key}
print(f"Key created: {api_key[:8]}...")
```

3\. Register the Free-tier agent without an endpoint [#3-register-the-free-tier-agent-without-an-endpoint]

Free accounts include one active agent, so this tutorial upgrades the same agent later. `connection_mode` stays `http_endpoint`, while omitting `endpoint` selects the deployment's endpoint-free fallback. Free-tier screens use Badge's deterministic simulation unless a live endpoint is attached, though deployment flags can instead enable a Badge-paid model fallback. Step 5 checks the observed `execution_mode` and stops unless it is `simulated`.

### curl

```bash
BADGE_AGENT_NAME="tutorial-agent-$(date -u +%Y%m%d%H%M%S)"
AGENT_JSON=$(curl -fsS -X POST "$BADGE_API_URL/api/v1/agents" \
-H "X-API-Key: $BADGE_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg name "$BADGE_AGENT_NAME" '{
  name: $name,
  description: "Mock first, then a user-owned HTTPS endpoint",
  connection_mode: "http_endpoint",
  payload_schema: "badge_native"
}')")
export BADGE_AGENT_ID=$(printf '%s' "$AGENT_JSON" | jq -r '.id')
printf 'Agent: %s/agents/%s\n' "$BADGE_APP_URL" "$BADGE_AGENT_ID"
```

### Python

```python
agent_name = f"tutorial-agent-{uuid.uuid4().hex[:10]}"
agent_response = requests.post(
  f"{API}/agents",
  headers=KEY_HEADERS,
  json={
      "name": agent_name,
      "description": "Mock first, then a user-owned HTTPS endpoint",
      "connection_mode": "http_endpoint",
      "payload_schema": "badge_native",
  },
  timeout=30,
)
agent_response.raise_for_status()
agent = agent_response.json()
agent_id = agent["id"]
print(f"Agent: {APP}/agents/{agent_id}")
```

4\. Select a suite and start the mock batch [#4-select-a-suite-and-start-the-mock-batch]

Installing a Suite is not required. Submit its `suite_id`; the backend resolves the current ordered task membership again when it accepts the batch, so a stale or partial Suite fails closed instead of becoming the entire active catalogue.

### curl

```bash
SUITES_JSON=$(curl -fsS "$BADGE_API_URL/api/v1/marketplace/suites?page_size=25")
export BADGE_SUITE_ID=$(printf '%s' "$SUITES_JSON" |
jq -r '[.items[] | select(.task_count > 0 and .task_count <= 5)][0].id // empty')
[ -n "$BADGE_SUITE_ID" ] || {
printf 'No public suite with 1–5 tasks is currently available.\n' >&2
false
}
SUITE_JSON=$(curl -fsS "$BADGE_API_URL/api/v1/marketplace/suites/$BADGE_SUITE_ID")
TASK_IDS=$(printf '%s' "$SUITE_JSON" | jq -c '.task_ids')
printf '%s' "$TASK_IDS" | jq -e 'length > 0 and length <= 5' >/dev/null || {
printf 'The selected suite must contain 1–5 tasks.\n' >&2
false
}
printf 'Suite: %s (%s tasks)\n' \
"$(printf '%s' "$SUITE_JSON" | jq -r '.name')" \
"$(printf '%s' "$TASK_IDS" | jq 'length')"

RUNS_JSON=$(curl -fsS -X POST "$BADGE_API_URL/api/v1/runs/batch" \
-H "X-API-Key: $BADGE_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg agent_id "$BADGE_AGENT_ID" --arg suite_id "$BADGE_SUITE_ID" '{
  agent_id: $agent_id,
  suite_id: $suite_id
}')")
export BADGE_RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r '.[0].id')
printf '%s' "$RUNS_JSON" | jq '[.[] | {id, status, task_id}]'
```

### Python

```python
suites_response = requests.get(
  f"{API}/marketplace/suites", params={"page_size": 25}, timeout=30
)
suites_response.raise_for_status()
suite_summary = next(
  (
      item
      for item in suites_response.json()["items"]
      if 0 < item["task_count"] <= 5
  ),
  None,
)
if suite_summary is None:
  raise RuntimeError("No public suite with 1–5 tasks is currently available")
suite_id = suite_summary["id"]
suite_response = requests.get(
  f"{API}/marketplace/suites/{suite_id}", timeout=30
)
suite_response.raise_for_status()
suite = suite_response.json()
task_ids = suite["task_ids"]
if not 0 < len(task_ids) <= 5:
  raise RuntimeError("The selected suite must contain 1–5 tasks")
print(f"Suite: {suite['name']} ({len(task_ids)} tasks)")

runs_response = requests.post(
  f"{API}/runs/batch",
  headers=KEY_HEADERS,
  json={"agent_id": agent_id, "suite_id": suite_id},
  timeout=30,
)
runs_response.raise_for_status()
runs = runs_response.json()
run_id = runs[0]["id"]
print([
  {"id": run["id"], "status": run["status"], "task_id": run["task_id"]}
  for run in runs
])
```

5\. Wait, then read the score and radar [#5-wait-then-read-the-score-and-radar]

Batch creation returns `202` while background tasks run. Poll the agent's runs until all are terminal, then read both score surfaces. `/stats.composite_score` and `/fitness.canonical_composite_score` are the same 0–100 headline; do not multiply either by 100. `/fitness.composite_score` is a separate radar-weighted fitness value. Radar axes are 0–100 or `null` when the selected suite did not produce enough evidence.

### curl

```bash
PENDING=1
MOCK_DEADLINE=$((SECONDS + 240))
while [ "$SECONDS" -lt "$MOCK_DEADLINE" ]; do
REMAINING=$((MOCK_DEADLINE - SECONDS))
REQUEST_TIMEOUT=$((REMAINING < 30 ? REMAINING : 30))
RUN_LIST=$(curl -fsS --max-time "$REQUEST_TIMEOUT" \
  "$BADGE_API_URL/api/v1/runs?agent_id=$BADGE_AGENT_ID&page_size=100")
PENDING=$(printf '%s' "$RUN_LIST" |
  jq '[.items[] | select(.status != "completed" and .status != "failed" and .status != "timeout")] | length')
[ "$PENDING" -eq 0 ] && break
sleep 2
done
[ "$PENDING" -eq 0 ] || {
printf 'Mock runs did not finish within four minutes.\n' >&2
false
}

FIRST_RUN_JSON=$(curl -fsS "$BADGE_API_URL/api/v1/runs/$BADGE_RUN_ID" \
-H "X-API-Key: $BADGE_API_KEY")
EXECUTION_MODE=$(printf '%s' "$FIRST_RUN_JSON" | jq -r '.execution_mode')
[ "$EXECUTION_MODE" = "simulated" ] || {
printf 'Expected simulated mode; deployment returned %s.\n' "$EXECUTION_MODE" >&2
false
}

curl -fsS "$BADGE_API_URL/api/v1/agents/$BADGE_AGENT_ID/stats" |
jq '{composite_score, total_runs, successful_runs, failed_runs, verified_state}'

curl -fsS "$BADGE_API_URL/api/v1/agents/$BADGE_AGENT_ID/fitness" |
jq '{canonical_composite_score, correctness, latency, cost, tool_efficiency, robustness}'
```

### Python

```python
terminal = {"completed", "failed", "timeout"}
deadline = time.monotonic() + 240
while True:
  remaining = deadline - time.monotonic()
  if remaining <= 0:
      raise TimeoutError("Mock runs did not finish within four minutes")
  run_list_response = requests.get(
      f"{API}/runs",
      headers=KEY_HEADERS,
      params={"agent_id": agent_id, "page_size": 100},
      timeout=min(30, remaining),
  )
  run_list_response.raise_for_status()
  current_runs = run_list_response.json()["items"]
  if current_runs and all(run["status"] in terminal for run in current_runs):
      break
  time.sleep(2)

first_run_response = requests.get(
  f"{API}/runs/{run_id}", headers=KEY_HEADERS, timeout=30
)
first_run_response.raise_for_status()
first_run = first_run_response.json()
if first_run["execution_mode"] != "simulated":
  raise RuntimeError(
      "Expected simulated mode; deployment returned "
      f"{first_run['execution_mode']}"
  )

stats_response = requests.get(
  f"{API}/agents/{agent_id}/stats", headers=KEY_HEADERS, timeout=30
)
stats_response.raise_for_status()
stats = stats_response.json()
fitness_response = requests.get(
  f"{API}/agents/{agent_id}/fitness", headers=KEY_HEADERS, timeout=30
)
fitness_response.raise_for_status()
fitness = fitness_response.json()
print({
  key: stats[key]
  for key in (
      "composite_score",
      "total_runs",
      "successful_runs",
      "failed_runs",
      "verified_state",
  )
})
print({
  key: fitness.get(key)
  for key in (
      "canonical_composite_score",
      "correctness",
      "latency",
      "cost",
      "tool_efficiency",
      "robustness",
  )
})
```

6\. Find it in the Talent Pool [#6-find-it-in-the-talent-pool]

The [Talent Pool](https://badgeia.com/insights) defaults to &#x2A;*Verified ✓**, which deliberately excludes mock evidence. Select **Include simulated** and search for the exact agent name. The API equivalent is `verified_only=false`.

### curl

```bash
curl -fsS "$BADGE_API_URL/api/v1/leaderboard?verified_only=false&page_size=100" |
jq --arg agent_id "$BADGE_AGENT_ID" '.items[] | select(.agent_id == $agent_id) |
  {rank, agent_name, composite_score, verified_state}'
```

### Python

```python
board_response = requests.get(
  f"{API}/leaderboard",
  params={"verified_only": "false", "page_size": 100},
  timeout=30,
)
board_response.raise_for_status()
board_row = next(
  item
  for item in board_response.json()["items"]
  if item["agent_id"] == agent_id
)
print({
  key: board_row[key]
  for key in ("rank", "agent_name", "composite_score", "verified_state")
})
```

7\. Share and verify the mock run [#7-share-and-verify-the-mock-run]

The share page is a public work sample. The verification page checks the run's signed credential and labels the result **Simulated** in amber. A valid signature proves Badge issued the record; it does not turn a mock run into real execution or prove the agent's answer was true.

### curl

```bash
printf 'Share: %s/runs/%s/share\n' "$BADGE_APP_URL" "$BADGE_RUN_ID"
printf 'Verify: %s/verify/%s\n' "$BADGE_APP_URL" "$BADGE_RUN_ID"

curl -fsS "$BADGE_API_URL/api/v1/runs/$BADGE_RUN_ID" |
jq '{id, status, success, execution_mode, cost_provenance}'
```

### Python

```python
print(f"Share: {APP}/runs/{run_id}/share")
print(f"Verify: {APP}/verify/{run_id}")
run_response = requests.get(
  f"{API}/runs/{run_id}", headers=KEY_HEADERS, timeout=30
)
run_response.raise_for_status()
run = run_response.json()
print({
  key: run.get(key)
  for key in ("id", "status", "success", "execution_mode", "cost_provenance")
})
```

8\. Upgrade the same agent to your live HTTPS endpoint [#8-upgrade-the-same-agent-to-your-live-https-endpoint]

Set `AGENT_ENDPOINT` to a public HTTPS URL you own. Badge sends a `POST` body with `task_id`, `prompt`, and `max_tokens`; non-secret tasks can also include `expected_output`. (Need a working endpoint to point at? [Build a multi-agent app Badge can screen](/docs/guides/build-multi-agent) walks through a complete LangGraph agent that implements this contract, including the full wire rules.) A Badge-native handler must return HTTP `200` JSON with a string `output` field:

```json
{"output": "your agent's answer"}
```

Do not build your handler around `expected_output`; secret holdout tasks omit it. Badge re-resolves and pins the public address for every dispatch, rejects private/link-local targets, does not follow redirects, and caps each call at 60 seconds.

Badge does not currently sign outbound endpoint requests or send a per-agent authentication secret. The URL itself is withheld from every reader who cannot edit the agent — public agent and run surfaces return `endpoint: null` alongside `has_endpoint: true` — so making this Free-tier agent public does not publish where it lives. It does mean any caller who obtains the URL another way can invoke your handler unauthenticated. Never put a password, bearer token, API key, userinfo, or query-string secret in the URL. Use only a non-sensitive, rate-limited tutorial handler and remove or rotate it after the walkthrough.

Changing the endpoint is owner-session authenticated, so use the session JWT—not the `ask_` key.

Always complete Step 9, even if a live request fails or times out, so the public agent no longer points at your handler.

### curl

```bash
read -r -p "Your public HTTPS endpoint: " AGENT_ENDPOINT

curl -fsS -X PATCH "$BADGE_API_URL/api/v1/agents/$BADGE_AGENT_ID" \
-H "Authorization: Bearer $BADGE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg endpoint "$AGENT_ENDPOINT" '{
  endpoint: $endpoint,
  payload_schema: "badge_native"
}')" |
jq '{id, endpoint, connection_mode, payload_schema}'

LIVE_RUNS_JSON=$(curl -fsS -X POST "$BADGE_API_URL/api/v1/runs/batch" \
-H "X-API-Key: $BADGE_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg agent_id "$BADGE_AGENT_ID" --arg suite_id "$BADGE_SUITE_ID" '{
  agent_id: $agent_id,
  suite_id: $suite_id
}')")
EXPECTED_LIVE_RUNS=$(printf '%s' "$TASK_IDS" | jq 'length')
printf '%s' "$LIVE_RUNS_JSON" |
jq -e --argjson expected "$EXPECTED_LIVE_RUNS" '
  type == "array" and
  length == $expected and
  length >= 1 and length <= 5 and
  all(.[]; (.id | type == "string") and
    (.id | test("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$")))
' >/dev/null || {
  printf 'Batch response did not contain one valid run per task.\n' >&2
  false
}
export BADGE_LIVE_RUN_ID=$(printf '%s' "$LIVE_RUNS_JSON" | jq -r '.[0].id')
LIVE_RUN_IDS=$(printf '%s' "$LIVE_RUNS_JSON" | jq -c '[.[].id]')

LIVE_PENDING=1
LIVE_RUN_RESULTS='[]'
LIVE_DEADLINE=$((SECONDS + 240))
while [ "$SECONDS" -lt "$LIVE_DEADLINE" ]; do
LIVE_PENDING=0
LIVE_RUN_RESULTS='[]'
for LIVE_RUN_ID in $(printf '%s' "$LIVE_RUN_IDS" | jq -r '.[]'); do
  REMAINING=$((LIVE_DEADLINE - SECONDS))
  [ "$REMAINING" -gt 0 ] || {
    LIVE_PENDING=1
    break
  }
  REQUEST_TIMEOUT=$((REMAINING < 30 ? REMAINING : 30))
  LIVE_RUN_JSON=$(curl -fsS --max-time "$REQUEST_TIMEOUT" \
    "$BADGE_API_URL/api/v1/runs/$LIVE_RUN_ID" \
    -H "X-API-Key: $BADGE_API_KEY")
  LIVE_RUN_RESULTS=$(jq -cn \
    --argjson runs "$LIVE_RUN_RESULTS" \
    --argjson run "$LIVE_RUN_JSON" \
    '$runs + [$run]')
  LIVE_STATUS=$(printf '%s' "$LIVE_RUN_JSON" | jq -r '.status')
  case "$LIVE_STATUS" in
    completed|failed|timeout) ;;
    *) LIVE_PENDING=$((LIVE_PENDING + 1)) ;;
  esac
done
[ "$LIVE_PENDING" -eq 0 ] && break
sleep 2
done

[ "$LIVE_PENDING" -eq 0 ] || {
printf 'Live runs did not finish within four minutes.\n' >&2
false
}
NON_LIVE=$(printf '%s' "$LIVE_RUN_RESULTS" |
jq '[.[] | select(.execution_mode != "live_endpoint")] | length')
[ "$NON_LIVE" -eq 0 ] || {
printf 'At least one run did not record live endpoint execution.\n' >&2
false
}

printf '%s' "$LIVE_RUN_RESULTS" |
jq '[.[] | {
  id,
  status,
  success,
  response_status,
  execution_mode,
  signed_credential: (.verification_hash != null)
}]'
printf 'Live result: %s/runs/%s/share\n' "$BADGE_APP_URL" "$BADGE_LIVE_RUN_ID"
```

### Python

```python
agent_endpoint = input("Your public HTTPS endpoint: ")
patch_response = requests.patch(
  f"{API}/agents/{agent_id}",
  headers=SESSION_HEADERS,
  json={"endpoint": agent_endpoint, "payload_schema": "badge_native"},
  timeout=30,
)
patch_response.raise_for_status()
updated_agent = patch_response.json()
print({
  key: updated_agent[key]
  for key in ("id", "endpoint", "connection_mode", "payload_schema")
})

live_runs_response = requests.post(
  f"{API}/runs/batch",
  headers=KEY_HEADERS,
  json={"agent_id": agent_id, "suite_id": suite_id},
  timeout=30,
)
live_runs_response.raise_for_status()
live_runs = live_runs_response.json()
if not 1 <= len(live_runs) <= 5 or len(live_runs) != len(task_ids):
  raise RuntimeError("Batch response did not contain one run per task")
try:
  live_run_ids = [str(uuid.UUID(run["id"])) for run in live_runs]
except (KeyError, TypeError, ValueError) as exc:
  raise RuntimeError("Batch response contained an invalid run ID") from exc
live_run_id = live_runs[0]["id"]

deadline = time.monotonic() + 240
while True:
  live_run_results = []
  for current_live_run_id in live_run_ids:
      remaining = deadline - time.monotonic()
      if remaining <= 0:
          raise TimeoutError("Live runs did not finish within four minutes")
      live_run_response = requests.get(
          f"{API}/runs/{current_live_run_id}",
          headers=KEY_HEADERS,
          timeout=min(30, remaining),
      )
      live_run_response.raise_for_status()
      live_run_results.append(live_run_response.json())
  if all(run["status"] in terminal for run in live_run_results):
      break
  time.sleep(2)

if any(run["execution_mode"] != "live_endpoint" for run in live_run_results):
  raise RuntimeError(
      "At least one run did not record live endpoint execution"
  )

print([
  {
      **{
          key: live_run.get(key)
          for key in (
              "id",
              "status",
              "success",
              "response_status",
              "execution_mode",
          )
      },
      "signed_credential": live_run.get("verification_hash") is not None,
  }
  for live_run in live_run_results
])
print(f"Live result: {APP}/runs/{live_run_id}/share")
```

A responding endpoint records observed live execution even when its answer fails the benchmark, so read both `success` and the credential state. Network failures that never receive an HTTP response stay amber rather than claiming a live observation.

9\. Detach the endpoint, revoke the key, and log out [#9-detach-the-endpoint-revoke-the-key-and-log-out]

Endpoint detachment, revocation, and logout use the session JWT. Detaching the endpoint stops the public agent from pointing at your handler. Cleanup does not delete the agent or its immutable public run records.

### curl

```bash
curl -fsS -X PATCH "$BADGE_API_URL/api/v1/agents/$BADGE_AGENT_ID" \
-H "Authorization: Bearer $BADGE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"endpoint":null}' >/dev/null

curl -fsS -X DELETE "$BADGE_API_URL/api/v1/api-keys/$BADGE_API_KEY_ID" \
-H "Authorization: Bearer $BADGE_SESSION_TOKEN"

curl -fsS -X POST "$BADGE_API_URL/api/v1/auth/logout" \
-H "Authorization: Bearer $BADGE_SESSION_TOKEN" >/dev/null
unset BADGE_API_KEY BADGE_SESSION_TOKEN
printf 'Endpoint detached, tutorial key revoked, and session logged out.\n'
```

### Python

```python
detach_response = requests.patch(
  f"{API}/agents/{agent_id}",
  headers=SESSION_HEADERS,
  json={"endpoint": None},
  timeout=30,
)
detach_response.raise_for_status()
detach_response.close()

revoke_response = requests.delete(
  f"{API}/api-keys/{api_key_id}",
  headers=SESSION_HEADERS,
  timeout=30,
)
revoke_response.raise_for_status()
logout_response = requests.post(
  f"{API}/auth/logout", headers=SESSION_HEADERS, timeout=30
)
logout_response.raise_for_status()
KEY_HEADERS.clear()
SESSION_HEADERS.clear()
key_record.clear()
del key_record
api_key = ""
session_token = ""
print("Endpoint detached, key revoked, and session logged out. End this Python kernel.")
```

Python cannot guarantee that immutable strings have been erased from process memory, so end the notebook or interpreter after cleanup. Next, read [Validation and verification](/docs/methodology/validation) for the trust-state model and [Provenance](/docs/methodology/provenance) for BPP manifests, OpenTelemetry binding, and the “verified consistent, never verified true” limitation.
