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 the verifier build. - Wait for the build — verifier-binary generation takes ~30–90 seconds. The plugin blocks on the build event (
dezycro await-event --event-types VERIFIER_STATUS_UPDATE, see Waiting on events); outside the plugin, retrypull_verifieror watch the feature page. AFAILEDbuild can be re-triggered with theretry_verifier_buildMCP tool. - 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",
"details": "GET /resources/123 returned 200 with shape {...} but spec expected {...}"
}
]
}
Each journey ends pass, fail, or skip. When the run is uploaded, Dezycro classifies every non-passing result with a verdict explaining the likely cause — verdicts show up in the workbook and the coverage report.
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 a bucketed projection of every operation in your spec:
missingBindings— operations declared in the spec that no journey could be bound toboundButNotExecutable— bound operations that never became runnable (e.g. a setup gap)executableButNotObserved— runnable operations that no run actually exercisedobservedButInconclusive— operations that ran but ended in anINCONCLUSIVE_*verdictdecisiveFailures— operations that failed with aFAIL_DECISIVE_*verdict (top 3 surfaced by/dezycro:verify)waivedManual— gaps you explicitly accepted viawaive_coverageflowGaps— dependency-flow gaps between operations; high-confidence gaps block the lifecyclelifecycleBlocking— a single boolean: does anything above block the feature from reachingVERIFIED?
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 "$(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.