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¶
- Publish your OpenAPI spec — via
/dezycro:publish-specormcp__dezycro__publish_feature_spec. This kicks off JEP generation (the binary build). - Wait for the build — JEP generation takes ~30–90 seconds. Poll
generation_statusor watch the feature page. - Pull the binary —
mcp__dezycro__pull_verifier(featureId, platform)returns a signed download URL. The Claude Code plugin caches the binary under.dezycro/bin/<contentHash>/dezycro-verify. - Load auth fixtures — secrets from
.dezycro/auth.local.jsonare resolved into environment variables (one per persona). - Run the binary —
./dezycro-verify run --all --base-url <apiUrl> --jsonwrites a structured result. - Upload the result —
mcp__dezycro__record_verifier_run(workspaceId, featureId, results)posts the run; the workbook updates journey statuses and may auto-transition the feature toVERIFIED.
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
authconfig — 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 forgcloud auth print-access-tokenoraws 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:
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.