Skip to content

The Verifier

The Dezycro verifier is a standalone binary generated per-feature from the feature's OpenAPI spec + workbook journeys. It runs locally (or in CI) against your service, calls the journey steps, asserts the responses, and uploads a structured result back to the workbook.

The verifier is the closing-the-loop step of Dezycro: PRDs/TRDs say what should happen, the workbook tracks what was built, the verifier proves it actually works.

How a verifier run works

  1. Publish your OpenAPI spec — via /dezycro:publish-spec or mcp__dezycro__publish_feature_spec. This kicks off JEP generation (the binary build).
  2. Wait for the build — JEP generation takes ~30–90 seconds. Poll generation_status or watch the feature page.
  3. Pull the binarymcp__dezycro__pull_verifier(featureId, platform) returns a signed download URL. The Claude Code plugin caches the binary under .dezycro/bin/<contentHash>/dezycro-verify.
  4. Load auth fixtures — secrets from .dezycro/auth.local.json are resolved into environment variables (one per persona).
  5. Run the binary./dezycro-verify run --all --base-url <apiUrl> --json writes a structured result.
  6. Upload the resultmcp__dezycro__record_verifier_run(workspaceId, featureId, results) posts the run; the workbook updates journey statuses and may auto-transition the feature to VERIFIED.

The Claude Code plugin's /dezycro:verify skill does all six steps in one command.

Personas & auth.local.json

Most non-trivial APIs require authentication, and most journeys exercise multiple user identities — an admin creating a resource, then a regular user reading it, then a second user trying (and failing) to delete it. Dezycro models these as personas.

Each persona has:

  • A name (e.g. admin1, customer_a, org2_owner)
  • An auth config — how the verifier obtains a credential for this persona at run time
  • An envVar — the name of the environment variable the verifier reads the credential from

auth.local.json schema

The file lives at .dezycro/auth.local.json in your repo and is never committed (it contains secrets). Schema overview:

{
  "personas": {
    "admin1": {
      "envVar": "AUTH_ADMIN1",
      "auth": {
        "type": "bearer",
        "token": "!env:ADMIN_TOKEN"
      }
    },
    "customer_a": {
      "envVar": "AUTH_CUSTOMER_A",
      "auth": {
        "type": "http_endpoint",
        "url": "${API_BASE}/auth/login",
        "method": "POST",
        "body": {
          "email": "alice@example.com",
          "password": "!file:./secrets/alice-pw.txt"
        },
        "extract": "$.access_token"
      }
    }
  }
}

Auth types

Type Use when
bearer You have a static token / API key
basic HTTP Basic auth (user:pass)
header Custom header name + value
http_endpoint Hit a login endpoint, extract the token from the response
js Custom JS — for OAuth dances or complex token exchanges
none Unauthenticated journey

Secret backends

Secret values support three resolvers:

  • !file:<path> — read the file contents (recommended for tokens in ~/.dezycro/secrets/)
  • !env:<NAME> — read an environment variable
  • !cmd:<command> — run a shell command, use stdout (good for gcloud auth print-access-token or aws sts ...)

Substitutions: ${VAR} anywhere in a string is replaced from the process environment.

Loading the file

The plugin ships dezycro-resolve-auth — a binary that reads .dezycro/auth.local.json, resolves all secrets, and prints export KEY='value' lines for eval:

eval "$(dezycro-resolve-auth)"

The /dezycro:verify skill does this automatically. For CI / standalone usage, source it yourself before running the verifier binary.

Per-persona failures don't block

If one persona's secret can't be resolved (e.g. a !file: points at a missing file), the resolver prints a stderr warning for that persona and emits nothing for it — other personas still load cleanly. Journeys that need the missing persona return INCONCLUSIVE_AUTH_REQUIRED in the verifier output; unrelated journeys still run.

Verifier output

The binary writes JSON with per-journey results:

{
  "metadata": { "specHash": "...", "flowVersion": "...", "jepVersion": "..." },
  "journeys": [
    {
      "id": "JOURNEY-1",
      "name": "Create resource as admin",
      "status": "PASS",
      "steps": [ /* per-step assertion results */ ]
    },
    {
      "id": "JOURNEY-2",
      "name": "Read resource as customer",
      "status": "FAIL",
      "verdict": "FAIL_DECISIVE_CONTRACT",
      "details": "GET /resources/123 returned 200 with shape {...} but spec expected {...}"
    }
  ]
}

Failure verdicts

Verdict What it means Fix
FAIL_DECISIVE_ROUTE_MISSING Endpoint isn't implemented Implement it or remove from the spec
FAIL_DECISIVE_CONTRACT Response shape doesn't match spec Fix the endpoint or update the spec
FAIL_DECISIVE_PERSISTENCE State didn't persist Investigate the write path
INCONCLUSIVE_AUTH_REQUIRED Persona's credential couldn't be loaded Add auth fixture for that persona
INCONCLUSIVE_SETUP_REQUIRED A prerequisite state is missing Supply parent resource / fixture
INCONCLUSIVE_INPUT_CONSTRAINT Test input didn't satisfy a constraint Provide a valid fixture for the parameter

Coverage

After every run, get_coverage_report(workspaceId, featureId) returns:

  • Uncovered paths — endpoints in the spec with no journey hitting them
  • Endpoints not in spec — endpoints observed during execution but missing from the OpenAPI
  • Stale baselines — recorded baselines older than staleBaselineDays (default 14)
  • Decisive failures — journeys that decisively failed (top 3 surfaced by /dezycro:verify)
  • Inconclusive results — journeys that couldn't run, ranked by likely cause

The Claude Code plugin's coverage nudges read this report to surface gaps before a push.

Running the verifier in CI

The verifier is a single binary, so CI usage is straightforward:

- name: Pull verifier
  run: |
    curl -fsSL -H "Authorization: Bearer $DEZYCRO_PAT" \
      "https://api.dezycro.ai/api/v1/.../verifier" -o dezycro-verify
    chmod +x dezycro-verify

- name: Resolve auth
  run: |
    eval "$(npx dezycro-resolve-auth)"
    echo "AUTH env vars loaded for $(echo $AUTH_* | tr ' ' '\n' | wc -l) personas"

- name: Run
  run: ./dezycro-verify run --all --base-url $API_URL --json > results.json

- name: Upload result
  run: |
    curl -fsSL -X POST -H "Authorization: Bearer $DEZYCRO_PAT" \
      -H "Content-Type: application/json" \
      -d @results.json \
      "https://api.dezycro.ai/api/v1/.../runs"

The Claude Code plugin handles all of this in one command via /dezycro:verify; CI is where you'd shell it out yourself.