- P0: CLI verify now honors test budget with seeded multi-sample - P0: Observe sampling enforced via Math.random() gate in hook-validator - P1: Remove misleading undici-mock-agent isolation option - P1: Qualify reuses shared discoverRouteDetails() with warnings - P1: Chaos/scenario config exposed via preset schema - P1: README/docs limitations updated to current state - P2: Nested response annotations prefer 2xx deterministically - P2: --changed documented as heuristic in verify.md - Add observe sink tests (sampling 0/1, sink failure non-interference) - Add verify runs regression tests (scale, determinism, variants) - Add configured-scenario qualify test (independent of OAuth fixture) - Add coverageBreakdown to qualify artifacts (per-gate route coverage) - Add production-style observe example with real sink in docs/observe.md - Add nightly/staging vs PR gating guidance to docs/qualify.md - Enrich VerifyFailure with formula-aware diagnostics: status:201 => 'HTTP 200', body field checks => actual values - Remove stale observe CLI activation message - Document outbound mocks as process-global in getting-started.md - Refresh APOPHIS_ADOPTION_AUDIT.md with current state 903 tests pass, build clean, typecheck clean.
4.7 KiB
Verify Mode
Deterministic contract verification for CI and local development.
APOPHIS implements invariant-driven testing: encode intended behavior as executable formulas, then verify them automatically with property-based generation and deterministic replay.
When to Use It
- Local development: Quick feedback on behavioral changes
- CI pipelines: Catch regressions before merge
- Pre-commit hooks: Fast smoke verification
Profile Examples
Quick (local smoke)
profiles: {
quick: {
name: 'quick',
mode: 'verify',
preset: 'safe-ci',
routes: ['POST /users']
}
}
CI (PR checks)
profiles: {
ci: {
name: 'ci',
mode: 'verify',
preset: 'safe-ci',
routes: []
}
}
Run with: apophis verify --profile ci --changed
Deep (nightly verification)
profiles: {
deep: {
name: 'deep',
mode: 'verify',
preset: 'safe-ci',
routes: []
}
}
Route Filtering
Filter routes with the --routes flag:
# Single route
apophis verify --routes "POST /users"
# Multiple routes (comma-separated)
apophis verify --routes "POST /users,PUT /users/:id"
# Wildcards
apophis verify --routes "POST /users/*"
# All routes (empty or omitted)
apophis verify --profile quick
* and ? wildcards are supported in --routes.
--changed Flag
Run only routes modified in the current git branch:
apophis verify --profile ci --changed
If no routes changed, exits 2 with a message.
--changed is a heuristic: it maps changed file paths to routes by checking route path segments against file names. This is useful as a developer convenience, but for strict CI gating, prefer explicit --routes filters or full verification.
Failure Output Format
When a contract fails, APOPHIS prints:
Contract violation
POST /users
Profile: quick
Seed: 42
Expected
response_code(GET /users/{response_body(this).id}) == 200
Observed
GET /users/usr-123 returned 404
Why this matters
The resource created by POST /users is not retrievable.
Replay
apophis replay --artifact reports/apophis/failure-2026-04-28T12-30-22Z.json
Next
Check the create/read consistency for POST /users and GET /users/{id}.
Replay Workflow
- Copy the replay command from failure output
- Run it with the recorded route, seed, and artifact data; source or dependency drift can change the outcome
- Fix the bug in your handler
- Re-run verify to confirm the fix
apophis replay --artifact reports/apophis/failure-2026-04-28T12-30-22Z.json
Nondeterminism warnings appear in output when the same seed produces different results across runs. This indicates stateful behavior in your application that contracts cannot control.
Machine Output for CI
Use concise formats to reduce log volume in large verify runs:
--format json-summary— single JSON with summary, failures, warnings. Omits per-step traces.--format ndjson-summary— three NDJSON lines:run.started,run.summary,run.completed.
Filtering examples
# Extract only failed routes from full ndjson
# Note: route.failed events are only emitted for failures, not passed routes
apophis verify --profile quick --format ndjson | jq 'select(.type == "route.failed")'
# Write artifact to disk and parse the file instead of stdout
apophis verify --profile quick --format json --artifact-dir reports/apophis
Exit Codes
| Code | Meaning |
|---|---|
| 0 | All contracts passed |
| 1 | One or more behavioral contracts failed |
| 2 | Config error, no routes matched, no contracts found, or not a git repo |
| 3 | Internal APOPHIS error |
| 130 | Interrupted (SIGINT) |
Config Example
// apophis.config.js
export default {
profile: 'quick',
profiles: {
quick: {
mode: 'verify',
preset: 'safe-ci',
routes: ['POST /users']
}
},
presets: {
'safe-ci': {
runs: 10,
timeout: 5000
}
}
};
For the full config schema, see CLI Reference.
Workspace Support
Run verify across all packages in a monorepo workspace:
apophis verify --workspace --profile quick --format json
Output includes per-package pass/fail summaries. Fails if any package fails.
Test Budget
The runs field in your preset controls how many property-based test samples execute per route. Default is 50. Lower for faster CI feedback, higher for deeper exploration:
profiles: {
quick: {
mode: 'verify',
preset: 'safe-ci',
routes: ['POST /users']
}
},
presets: {
'safe-ci': {
runs: 10,
timeout: 5000
}
}
CLI verify generates one property-based test sample per contract by default when no runs is specified. Set runs in the preset to increase sampled inputs per route.