@kirrosh/zond 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +648 -0
  2. package/README.md +58 -6
  3. package/package.json +9 -6
  4. package/src/cli/argv.ts +122 -0
  5. package/src/cli/commands/add-api.ts +134 -0
  6. package/src/cli/commands/api/annotate/idempotency.ts +59 -0
  7. package/src/cli/commands/api/annotate/index.ts +525 -0
  8. package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
  9. package/src/cli/commands/api/annotate/overlay.ts +206 -0
  10. package/src/cli/commands/api/annotate/pagination.ts +60 -0
  11. package/src/cli/commands/api/annotate/prompts.ts +183 -0
  12. package/src/cli/commands/api/annotate/readback.ts +58 -0
  13. package/src/cli/commands/api/annotate/resources.ts +91 -0
  14. package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
  15. package/src/cli/commands/audit.ts +480 -0
  16. package/src/cli/commands/bootstrap.ts +710 -0
  17. package/src/cli/commands/catalog.ts +35 -0
  18. package/src/cli/commands/check.ts +348 -0
  19. package/src/cli/commands/checks.ts +756 -0
  20. package/src/cli/commands/ci-init.ts +43 -0
  21. package/src/cli/commands/clean.ts +212 -0
  22. package/src/cli/commands/cleanup.ts +262 -0
  23. package/src/cli/commands/completions.ts +16 -0
  24. package/src/cli/commands/coverage.ts +605 -132
  25. package/src/cli/commands/db.ts +178 -7
  26. package/src/cli/commands/describe.ts +37 -2
  27. package/src/cli/commands/discover.ts +1236 -0
  28. package/src/cli/commands/doctor.ts +607 -0
  29. package/src/cli/commands/fixtures.ts +402 -0
  30. package/src/cli/commands/generate.ts +420 -46
  31. package/src/cli/commands/init/bootstrap.ts +30 -1
  32. package/src/cli/commands/{init.ts → init/index.ts} +99 -5
  33. package/src/cli/commands/init/skills.ts +56 -3
  34. package/src/cli/commands/init/templates/agents.md +65 -61
  35. package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
  36. package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
  37. package/src/cli/commands/init/templates/skills/zond.md +592 -125
  38. package/src/cli/commands/init/templates/zond-config.yml +8 -9
  39. package/src/cli/commands/prepare-fixtures.ts +135 -0
  40. package/src/cli/commands/probe/mass-assignment.ts +503 -0
  41. package/src/cli/commands/probe/security.ts +454 -0
  42. package/src/cli/commands/probe/static.ts +255 -0
  43. package/src/cli/commands/probe/webhooks.ts +161 -0
  44. package/src/cli/commands/probe.ts +459 -0
  45. package/src/cli/commands/reference.ts +87 -0
  46. package/src/cli/commands/refresh-api.ts +169 -0
  47. package/src/cli/commands/remove-api.ts +150 -0
  48. package/src/cli/commands/report-bundle.ts +318 -0
  49. package/src/cli/commands/report.ts +241 -0
  50. package/src/cli/commands/request.ts +379 -4
  51. package/src/cli/commands/run.ts +842 -53
  52. package/src/cli/commands/session.ts +244 -0
  53. package/src/cli/commands/use.ts +18 -1
  54. package/src/cli/index.ts +20 -3
  55. package/src/cli/json-envelope.ts +112 -3
  56. package/src/cli/json-schemas.ts +263 -0
  57. package/src/cli/program.ts +198 -635
  58. package/src/cli/resolve.ts +105 -0
  59. package/src/cli/status-filter.ts +124 -0
  60. package/src/cli/util/api-context.ts +85 -0
  61. package/src/cli/version.ts +5 -0
  62. package/src/core/anti-fp/bootstrap.ts +34 -0
  63. package/src/core/anti-fp/index.ts +33 -0
  64. package/src/core/anti-fp/registry.ts +44 -0
  65. package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
  66. package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
  67. package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
  68. package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
  69. package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
  70. package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
  71. package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
  72. package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
  73. package/src/core/anti-fp/types.ts +68 -0
  74. package/src/core/checks/checks/_crud-helpers.ts +133 -0
  75. package/src/core/checks/checks/_negative_mutator.ts +133 -0
  76. package/src/core/checks/checks/_readback-helpers.ts +133 -0
  77. package/src/core/checks/checks/content_type_conformance.ts +39 -0
  78. package/src/core/checks/checks/cross_call_references.ts +134 -0
  79. package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
  80. package/src/core/checks/checks/idempotency_replay.ts +246 -0
  81. package/src/core/checks/checks/ignored_auth.ts +211 -0
  82. package/src/core/checks/checks/index.ts +65 -0
  83. package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
  84. package/src/core/checks/checks/missing_required_header.ts +40 -0
  85. package/src/core/checks/checks/negative_data_rejection.ts +45 -0
  86. package/src/core/checks/checks/not_a_server_error.ts +27 -0
  87. package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
  88. package/src/core/checks/checks/pagination_invariants.ts +238 -0
  89. package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
  90. package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
  91. package/src/core/checks/checks/response_headers_conformance.ts +74 -0
  92. package/src/core/checks/checks/response_schema_conformance.ts +30 -0
  93. package/src/core/checks/checks/status_code_conformance.ts +61 -0
  94. package/src/core/checks/checks/unsupported_method.ts +63 -0
  95. package/src/core/checks/checks/use_after_free.ts +78 -0
  96. package/src/core/checks/index.ts +30 -0
  97. package/src/core/checks/mode.ts +79 -0
  98. package/src/core/checks/recommended-action.ts +64 -0
  99. package/src/core/checks/registry.ts +78 -0
  100. package/src/core/checks/runner.ts +874 -0
  101. package/src/core/checks/sarif.ts +230 -0
  102. package/src/core/checks/stateful.ts +121 -0
  103. package/src/core/checks/types.ts +189 -0
  104. package/src/core/classifier/recommended-action.ts +222 -0
  105. package/src/core/context/current.ts +22 -6
  106. package/src/core/context/session.ts +78 -0
  107. package/src/core/coverage/loader.ts +185 -0
  108. package/src/core/coverage/reasons.ts +300 -0
  109. package/src/core/diagnostics/db-analysis.ts +151 -11
  110. package/src/core/diagnostics/failure-class.ts +120 -0
  111. package/src/core/diagnostics/failure-hints.ts +212 -9
  112. package/src/core/diagnostics/spec-pointer.ts +99 -0
  113. package/src/core/diagnostics/suggested-fixes.ts +156 -0
  114. package/src/core/exporter/case-study/index.ts +270 -0
  115. package/src/core/exporter/curl.ts +40 -0
  116. package/src/core/exporter/exporter.ts +48 -0
  117. package/src/core/exporter/html-report/escape.ts +24 -0
  118. package/src/core/exporter/html-report/index.ts +479 -0
  119. package/src/core/exporter/html-report/script.ts +100 -0
  120. package/src/core/exporter/html-report/styles.ts +408 -0
  121. package/src/core/generator/chunker.ts +42 -16
  122. package/src/core/generator/coverage-phase.ts +0 -0
  123. package/src/core/generator/create-body.ts +89 -0
  124. package/src/core/generator/data-factory.ts +445 -19
  125. package/src/core/generator/describe.ts +1 -1
  126. package/src/core/generator/fixtures-builder.ts +325 -0
  127. package/src/core/generator/index.ts +7 -5
  128. package/src/core/generator/openapi-reader.ts +37 -3
  129. package/src/core/generator/path-param-disambig.ts +114 -0
  130. package/src/core/generator/resources-builder.ts +648 -0
  131. package/src/core/generator/schema-utils.ts +11 -3
  132. package/src/core/generator/serializer.ts +103 -13
  133. package/src/core/generator/suite-generator.ts +419 -111
  134. package/src/core/generator/types.ts +8 -0
  135. package/src/core/identity/identity-file.ts +129 -0
  136. package/src/core/lint/affects.ts +28 -0
  137. package/src/core/lint/config.ts +96 -0
  138. package/src/core/lint/format.ts +42 -0
  139. package/src/core/lint/index.ts +94 -0
  140. package/src/core/lint/reporter.ts +128 -0
  141. package/src/core/lint/rules/consistency.ts +158 -0
  142. package/src/core/lint/rules/heuristics.ts +97 -0
  143. package/src/core/lint/rules/strictness.ts +109 -0
  144. package/src/core/lint/types.ts +96 -0
  145. package/src/core/lint/walker.ts +248 -0
  146. package/src/core/meta/meta-store.ts +6 -73
  147. package/src/core/output/README.md +91 -0
  148. package/src/core/output/index.ts +13 -0
  149. package/src/core/output/run.ts +126 -0
  150. package/src/core/output/types.ts +129 -0
  151. package/src/core/parser/env-interpolation.ts +104 -0
  152. package/src/core/parser/filter.ts +57 -0
  153. package/src/core/parser/schema.ts +129 -4
  154. package/src/core/parser/types.ts +19 -1
  155. package/src/core/parser/variables.ts +0 -0
  156. package/src/core/parser/yaml-parser.ts +58 -12
  157. package/src/core/probe/bootstrap.ts +34 -0
  158. package/src/core/probe/dry-run-envelope.ts +57 -0
  159. package/src/core/probe/mass-assignment-probe-class.ts +198 -0
  160. package/src/core/probe/mass-assignment-probe.ts +1122 -0
  161. package/src/core/probe/mass-assignment-template.ts +212 -0
  162. package/src/core/probe/method-probe.ts +43 -76
  163. package/src/core/probe/method-shared.ts +69 -0
  164. package/src/core/probe/negative-probe.ts +183 -149
  165. package/src/core/probe/orphan-tracker.ts +188 -0
  166. package/src/core/probe/path-discovery.ts +440 -0
  167. package/src/core/probe/probe-harness.ts +120 -0
  168. package/src/core/probe/registry.ts +89 -0
  169. package/src/core/probe/runner.ts +136 -0
  170. package/src/core/probe/security-probe-class.ts +201 -0
  171. package/src/core/probe/security-probe.ts +1453 -0
  172. package/src/core/probe/shared.ts +505 -0
  173. package/src/core/probe/static-probe-class.ts +125 -0
  174. package/src/core/probe/types.ts +165 -0
  175. package/src/core/probe/verdict-aggregator.ts +33 -0
  176. package/src/core/probe/webhooks-probe.ts +284 -0
  177. package/src/core/reporter/console.ts +41 -2
  178. package/src/core/reporter/index.ts +2 -3
  179. package/src/core/reporter/json.ts +11 -1
  180. package/src/core/reporter/junit.ts +27 -12
  181. package/src/core/reporter/ndjson.ts +37 -0
  182. package/src/core/reporter/types.ts +3 -0
  183. package/src/core/runner/assertions.ts +58 -1
  184. package/src/core/runner/async-pool.ts +108 -0
  185. package/src/core/runner/auth-path.ts +8 -0
  186. package/src/core/runner/ci-context.ts +72 -0
  187. package/src/core/runner/executor.ts +264 -20
  188. package/src/core/runner/form-encode.ts +51 -0
  189. package/src/core/runner/http-client.ts +75 -2
  190. package/src/core/runner/learn-drift.ts +293 -0
  191. package/src/core/runner/preflight-vars.ts +149 -0
  192. package/src/core/runner/progress-tracker.ts +73 -0
  193. package/src/core/runner/rate-limiter.ts +89 -17
  194. package/src/core/runner/run-kind.ts +39 -0
  195. package/src/core/runner/schema-validator.ts +312 -0
  196. package/src/core/runner/send-request.ts +153 -20
  197. package/src/core/runner/types.ts +38 -0
  198. package/src/core/secrets/registry.ts +164 -0
  199. package/src/core/secrets/secrets-file.ts +115 -0
  200. package/src/core/selectors/operation-filter.ts +144 -0
  201. package/src/core/setup-api.ts +415 -16
  202. package/src/core/severity/category.ts +94 -0
  203. package/src/core/severity/index.ts +121 -0
  204. package/src/core/spec/layers.ts +154 -0
  205. package/src/core/util/format-eta.ts +21 -0
  206. package/src/core/utils.ts +5 -1
  207. package/src/core/workspace/config.ts +129 -0
  208. package/src/core/workspace/manifest.ts +283 -0
  209. package/src/core/workspace/output-rotation.ts +62 -0
  210. package/src/core/workspace/triage-path.ts +87 -0
  211. package/src/db/lint-runs.ts +47 -0
  212. package/src/db/migrate.ts +126 -0
  213. package/src/db/migrations/0001_run_kind.sql +25 -0
  214. package/src/db/migrations/sql.d.ts +4 -0
  215. package/src/db/queries/collections.ts +133 -0
  216. package/src/db/queries/coverage.ts +9 -0
  217. package/src/db/queries/dashboard.ts +59 -0
  218. package/src/db/queries/results.ts +128 -0
  219. package/src/db/queries/runs.ts +235 -0
  220. package/src/db/queries/sessions.ts +42 -0
  221. package/src/db/queries/settings.ts +28 -0
  222. package/src/db/queries/types.ts +172 -0
  223. package/src/db/queries.ts +72 -802
  224. package/src/db/schema.ts +178 -50
  225. package/src/cli/commands/export.ts +0 -144
  226. package/src/cli/commands/guide.ts +0 -127
  227. package/src/cli/commands/init/templates/skills/scenarios.md +0 -97
  228. package/src/cli/commands/probe-methods.ts +0 -108
  229. package/src/cli/commands/probe-validation.ts +0 -124
  230. package/src/cli/commands/serve.ts +0 -114
  231. package/src/cli/commands/sync.ts +0 -268
  232. package/src/cli/commands/update.ts +0 -189
  233. package/src/cli/commands/validate.ts +0 -34
  234. package/src/core/diagnostics/render-md.ts +0 -112
  235. package/src/core/exporter/postman.ts +0 -963
  236. package/src/core/generator/guide-builder.ts +0 -253
  237. package/src/core/meta/types.ts +0 -19
  238. package/src/core/parser/index.ts +0 -21
  239. package/src/core/runner/execute-run.ts +0 -132
  240. package/src/core/runner/index.ts +0 -12
  241. package/src/core/sync/spec-differ.ts +0 -38
  242. package/src/web/data/collection-state.ts +0 -362
  243. package/src/web/routes/api.ts +0 -314
  244. package/src/web/routes/dashboard.ts +0 -350
  245. package/src/web/routes/runs.ts +0 -64
  246. package/src/web/schemas.ts +0 -121
  247. package/src/web/server.ts +0 -134
  248. package/src/web/static/htmx.min.cjs +0 -1
  249. package/src/web/static/style.css +0 -1148
  250. package/src/web/views/endpoints-tab.ts +0 -174
  251. package/src/web/views/explorer-tab.ts +0 -402
  252. package/src/web/views/health-strip.ts +0 -92
  253. package/src/web/views/layout.ts +0 -48
  254. package/src/web/views/results.ts +0 -210
  255. package/src/web/views/runs-tab.ts +0 -126
  256. package/src/web/views/suites-tab.ts +0 -181
@@ -1,184 +1,651 @@
1
1
  ---
2
2
  name: zond
3
3
  description: |
4
- End-to-end API testing with zond generate tests from an OpenAPI spec, run them,
5
- diagnose failures, and hunt for typical backend bugs via probe suites. Use when
6
- asked to: test an API, cover endpoints, raise coverage, find bugs, diagnose
7
- failed runs, fix failing tests, debug 4xx/5xx, run probes, sync tests after a
8
- spec change, set up API test infrastructure. Activates on: openapi.json,
9
- openapi.yaml, swagger.json, .api-catalog.yaml, "test this API", "cover this
10
- spec", "tests are failing", "diagnose run", "find bugs", "probe", "5xx",
11
- "negative input".
12
- allowed-tools: [Read, Write, Bash(zond *), Bash(bunx zond *)]
4
+ Primary skill for any zond work. Loads on workspace touch
5
+ (`apis/<name>/`, `.env.yaml`, `.api-fixtures.yaml`) AND on intent
6
+ full API audit, "find bugs", "raise coverage", "test the whole API",
7
+ contract-drift check, schema-drift detection, post-deploy regression,
8
+ case study, single-flow scenario authoring, fixture pack. Hand off to
9
+ `zond-checks` for depth-check reference / SARIF / stateful invariants,
10
+ to `zond-triage` for "what broke in the last run".
11
+ allowed-tools: [Read, Write, Edit, Bash(zond *), Bash(bunx zond *), Bash(sqlite3 *)]
13
12
  ---
14
13
 
15
- # zond — API Coverage, Diagnosis & Bug Hunting
14
+ # zond — Primary skill
15
+
16
+ CLI-only. Run `zond --version` first; if missing:
17
+ `curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh | sh`.
18
+
19
+ For depth-check reference (the catalog of conformance / security /
20
+ stateful checks, SARIF output, m-20 invariants) — see `zond-checks`.
21
+ For triage of a failing run — see `zond-triage`.
22
+
23
+ ## Iron rules (read once, apply always)
24
+
25
+ - **NEVER read raw OpenAPI** with Read/cat/grep. The workspace has
26
+ pre-built artifacts (catalog/resources/fixtures) — use those. Drop
27
+ into `apis/<name>/spec.json` only when probe-class authoring needs
28
+ full schemas.
29
+ - **NEVER `curl` or `wget`** — use `zond request <method> <url> --api <name>`
30
+ for ad-hoc HTTP so it lands in the run DB and respects auth. Never
31
+ shell-substitute the token by hand (`$(yq …)` is blocked by the sandbox).
32
+ - **NEVER hardcode tokens** — put them in `apis/<name>/.secrets.yaml`
33
+ (auto-gitignored), reference from `.env.yaml` as `@secret:auth_token`.
34
+ Plain `${SHELL_VAR}` references also work. Tests read the resolved
35
+ value as `{{auth_token}}`.
36
+ - **NEVER read `.secrets.yaml` directly.** Use `zond doctor --api <name> --json`
37
+ — reports `set | unset` and value length only.
38
+ - **NEVER edit `.api-*.yaml`** (catalog / resources / fixtures) by hand —
39
+ regenerated by `zond refresh-api`. `.api-resources.local.yaml` IS the
40
+ hand-editable / annotate-target overlay; safe to edit. `.env.yaml`
41
+ values are editable (see ARV-114).
42
+ - **NEVER invent endpoints.** Only use entries from `.api-catalog.yaml`.
43
+ - **NEVER run destructive ops on a shared / production org without
44
+ `--dry-run` first.** Probes, `prepare-fixtures --apply`, `cleanup` hit
45
+ live APIs and can delete user data. `--dry-run` once → inspect → drop
46
+ the flag.
47
+ - **NEVER share triage artefacts** outbound without `--redact-identity`.
48
+ Secret-redaction is on by default; identity values (org/member slugs,
49
+ real ids) need the extra flag.
50
+ - **NEVER report cleanup failure as an API bug.** POST 200 → DELETE 5xx
51
+ is *probably* a fixture-isolation issue (orphan accumulation, race).
52
+ Re-run with `--no-cleanup` or isolated namespace before filing.
53
+ - **`recommended_action: report_backend_bug` / 5xx → STOP** in interactive
54
+ mode (surface the request/response, get a decision; never `expect:`-mask).
55
+ In autonomous / loop / audit-sweep mode (no user-in-the-loop), log to
56
+ `api-bugs-<NN>.md`, continue the sweep — bailing on bug #1 forfeits #2..N.
57
+ - **CRUD-run ≥80% 401/403 / `permission_denied` → `env_issue`, not bug.**
58
+ Token scope issue. Confirm via `zond db diagnose <run-id> --env-only`;
59
+ do not generate case-studies, do not `expect:`-mask.
60
+ - **MUST run `zond doctor --api <name> --missing-only` before generating
61
+ fixtures or touching `.env.yaml`** — identifies unfilled keys early.
62
+ - **Cascade cap is 8 passes** (`zond prepare-fixtures --cascade [--seed]`)
63
+ — never override without a written reason.
64
+
65
+ ## Workspace artifact model
66
+
67
+ After `zond add api <name> --spec <path|url>`, `apis/<name>/` contains:
16
68
 
17
- CLI-only skill. zond is invoked directly via shell. Run `zond --version` first;
18
- if missing, install via `curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh | sh`.
69
+ ```
70
+ spec.json ← dereferenced OpenAPI (machine source)
71
+ .api-catalog.yaml ← endpoint index (method, path, params)
72
+ .api-resources.yaml ← CRUD chains + FK deps (auto)
73
+ .api-resources.local.yaml ← overlay: extensions + annotate output (hand-edit OK; appears after first `zond api annotate apply` — or create by hand to use as overlay)
74
+ .api-fixtures.yaml ← MANIFEST: required vars + descriptions (read-only)
75
+ .env.yaml ← VALUES: variable values (user / prepare-fixtures writes)
76
+ .secrets.yaml ← raw secret values (auto-gitignored)
77
+ .identity.yaml ← non-secret identifying values (gitignored)
78
+ tests/ ← generated/handwritten suites
79
+ scenarios/ ← handwritten flow suites
80
+ probes/ ← probe-emitted suites
81
+ ```
82
+
83
+ ### Manifest vs values (the #1 source of confusion)
84
+
85
+ **`.api-fixtures.yaml` = the list of vars this API needs (manifest).
86
+ `.env.yaml` = their values.**
87
+
88
+ | Op | Touches manifest? | Touches env? |
89
+ |---|---|---|
90
+ | `zond add api` / `refresh-api` | rebuilds | seeds skeleton |
91
+ | `zond generate` | extends (adds new `{{vars}}` discovered) | does **not** modify |
92
+ | `zond prepare-fixtures` | reads | writes values |
93
+ | `zond fixtures add` / `import` | reads | writes values (manual) |
94
+ | user editing | never | always |
95
+
96
+ - `{{var}}` in a generated test but NOT in `.api-fixtures.yaml` is a
97
+ bug in the manifest builder / generator. Don't "fix" it by adding to
98
+ `.env.yaml`.
99
+ - A key in `.env.yaml` but NOT in `.api-fixtures.yaml` is shadow —
100
+ `prepare-fixtures` warns `not in manifest, ignored`.
101
+ - "Generate should sync `.env.yaml`" is a rejected design (decision-7,
102
+ m-17).
103
+
104
+ ### Manual fixture-bootstrap (when `prepare-fixtures --seed` can't help)
105
+
106
+ `prepare-fixtures --seed` requires a discoverable list endpoint or a
107
+ postable create. Path-id ids that live only in a vendor dashboard
108
+ (Stripe `cus_*`, GitHub PR numbers, Sentry issue ids) need a manual
109
+ hand-off:
110
+
111
+ | Input you have | Command |
112
+ |---|---|
113
+ | Concrete id values | `zond fixtures add <var>=<id> [--validate] --apply` |
114
+ | Curl from devtools / dashboard | `pbpaste \| zond fixtures import --from-curl --apply` (URL is matched against spec paths to extract `{var}` bindings) |
115
+
116
+ `--validate` GETs the spec's read-by-id endpoint and classifies the
117
+ fixture as `live` / `stale` / `unknown` so dead ids don't silently
118
+ break later runs (ARV-32). Both commands write to `apis/<name>/.env.yaml`
119
+ with a `.env.yaml.bak` backup, mirroring `prepare-fixtures --apply`.
120
+
121
+ ### When to read which file
122
+
123
+ | To know… | Read |
124
+ |---|---|
125
+ | What endpoints exist | `.api-catalog.yaml` |
126
+ | How resources chain (CRUD, FK, ETag) | `.api-resources.yaml` |
127
+ | What vars are needed and why | `.api-fixtures.yaml` |
128
+ | What var values are set | `.env.yaml` |
129
+ | Annotation overlay (seed_body, lifecycle, pagination, readback_diff) | `.api-resources.local.yaml` |
130
+ | Full request/response schema (only when needed) | `spec.json` |
131
+
132
+ ## --json envelope (uniform across commands)
133
+
134
+ ```jsonc
135
+ {
136
+ "ok": true|false,
137
+ "command": "<name>",
138
+ "data": { /* command-specific */ },
139
+ "warnings": ["..."],
140
+ "errors": [{ "code": "ZondErrorCode", "message": "...", "details": {} }],
141
+ "exit_code": 0|1|2
142
+ }
143
+ ```
144
+
145
+ Route on `errors[].code`, not message. Stdout discipline: `--json`
146
+ emits only the envelope on stdout; progress/hints to stderr.
147
+
148
+ `--json` ≠ `--report json`. `--json` wraps the **command result**;
149
+ `--report json` wraps the **test-run artefact**. `zond run` doesn't
150
+ accept `--json` — use `--report json`.
151
+
152
+ ## Entry points — pick by intent
153
+
154
+ | User asked… | Start at |
155
+ |---|---|
156
+ | "audit this API", "test the whole API", "raise coverage" | Phase 0 → `zond audit --api <name>` (one-shot) OR walk Phase 0–8 |
157
+ | "find bugs", "test for 5xx", "probe sweep" | Phase 0 → Phase 7 (Probes) |
158
+ | "security only / SSRF / CRLF" | Phase 7.2 directly with `--dry-run` first |
159
+ | "deep depth-checks", "SARIF", "stateful invariants" | Hand off → `zond-checks` |
160
+ | "what broke in last run", "diagnose run X" | Hand off → `zond-triage` |
161
+ | "write a test for X flow", "smoke this checkout" | Phase S (Scenarios) |
162
+ | "what vars does this API need" | `zond doctor --api <name> --json` |
163
+ | "share results", "case study" | Phase 9 (Share) |
164
+ | "workspace looks messy" | `zond clean --api <name>` (dry-run → `--force`) |
165
+
166
+ ## Phase 0 — Orient
167
+
168
+ ```bash
169
+ zond doctor --api <name> --json # gaps + freshness
170
+ ```
171
+
172
+ Then read the three artifacts (NOT the raw spec):
173
+
174
+ ```bash
175
+ cat apis/<name>/.api-catalog.yaml | head -80
176
+ cat apis/<name>/.api-resources.yaml
177
+ cat apis/<name>/.api-fixtures.yaml
178
+ ```
179
+
180
+ If doctor reports stale → `zond refresh-api <name>`.
181
+
182
+ ## Phase 1 — Fixture pack
183
+
184
+ `zond prepare-fixtures` walks `.api-resources.yaml`, hits each owner
185
+ list endpoint, fills `.env.yaml`. Always `--apply`; add `--cascade --seed`
186
+ for empty workspaces.
187
+
188
+ ```bash
189
+ zond prepare-fixtures --api <name> # dry-run preview
190
+ zond prepare-fixtures --api <name> --apply # single-pass
191
+ zond prepare-fixtures --api <name> --apply --cascade --seed # cascade + POST-create when list is empty
192
+ zond prepare-fixtures --api <name> --refresh # = --verify --apply: drop stale ids, re-resolve
193
+ ```
194
+
195
+ `--seed` POSTs to create endpoints when a list returns `[]`, using a
196
+ schema-derived body. Bracket-notation nested params (Stripe's
197
+ `card[number]`, `items[0][price]`) are a known weak spot (ARV-196) —
198
+ some APIs will 400 here; **don't ask the user to fill missing ids
199
+ manually first**, jump to Phase 2 (annotate seed_body) instead.
19
200
 
20
- For multi-step user journeys / fixture creation through the API, hand off to
21
- `zond-scenarios` instead that is a different concern.
201
+ If `--seed` converges but vars stay UNSET, the reason is outside the
202
+ API path (email verify / paid plan / SCIM / TOS limits). Document the
203
+ gap; don't loop further.
22
204
 
23
- ## Critical rules (always-on)
205
+ Editing `.env.yaml` by hand is the sanctioned fallback when the CLI
206
+ cannot harvest a value (write-only ingest endpoints, SDK-only ids,
207
+ slugs that live in user's project config). Touch values only — never
208
+ add keys not in `.api-fixtures.yaml`.
24
209
 
25
- - **NEVER** open OpenAPI/Swagger files with Read/cat/grep — use `zond describe`,
26
- `zond catalog`, or the generated `.api-catalog.yaml`.
27
- - **NEVER** use curl/wget — use `zond request <method> <url>`.
28
- - **NEVER** write test YAML from scratch — start with `zond generate`, then edit failures.
29
- - **NEVER** hardcode tokens — `apis/<name>/.env.yaml` (auto-gitignored), reference as `{{auth_token}}`.
30
- - **`--safe` enforces GET-only** — required for first-pass smoke against unknown envs.
31
- - **`recommended_action: report_backend_bug` (5xx) → STOP**. Do NOT modify
32
- `expect: status` to make the test pass. Surface the bug to the user with the
33
- request/response excerpt.
34
- - **5xx in any run is a bug candidate** — never edit assertions to mask it.
35
- - For multi-suite tag filters always include the setup tag: `--tag crud,setup`.
36
- - Captures are file-scoped — pass auth across suites via a `setup: true` suite.
37
- - Re-run after each fix with `--safe --json`; do not batch many edits without verifying.
210
+ ## Phase 2 Annotate (NEW, m-20 / ARV-187)
38
211
 
39
- ## Entry points (skip phases when the request is narrow)
212
+ **Critical**: zond itself does NOT call any LLM. The flow is
213
+ **dump → agent fills YAML → apply**. The agent (you) generates the
214
+ overlay; zond validates and writes it into `.api-resources.local.yaml`.
40
215
 
41
- | User asked... | Start at phase | Skip |
216
+ `zond api annotate` has 6 dump-subcommands (one per aspect). Pick what
217
+ your downstream check needs:
218
+
219
+ | Aspect | Dump produces | Feeds into |
42
220
  |---|---|---|
43
- | "cover this API", "raise coverage", "test this spec" | 1 (Discover) | |
44
- | "find bugs", "probe this API", "test for 5xx" | 1 then 5 (Probes) | — |
45
- | "tests are failing", "diagnose run X", "fix failures" | 4 (Diagnose) | 1–3 |
46
- | "the run after my fix" | 3.x (Run) → 4 (Diagnose) | 1–2 |
221
+ | `--seed-bodies` | request-body schema per resource | `prepare-fixtures --seed`, all stateful checks |
222
+ | `--readback` | create+read pair (write-shape vs read-shape) | `cross_call_references` ignore_fields, write_to_read_map |
223
+ | `--idempotency` | header candidates | `idempotency_replay` (header, scope, body_hash_fields) |
224
+ | `--pagination` | list-endpoint cursor/page detection | `pagination_invariants` (type, cursor_param) |
225
+ | `--lifecycle` | action-endpoint candidates per resource | `lifecycle_transitions` (states, transitions) |
226
+ | `--resources` | orphan endpoints not in resource graph | `.api-resources.local.yaml` extensions |
227
+
228
+ **Workflow** (for ANY aspect):
229
+
230
+ ```bash
231
+ # 1. Dump — emits JSON slices on stdout. Restrict scope with --only.
232
+ zond api annotate dump --api <name> --seed-bodies --only r1,r2 > /tmp/dump.json
233
+
234
+ # 2. Agent reads the dump and writes a YAML response file (see format below).
235
+
236
+ # 3. Apply — dry-run first (diff), then --yes to write.
237
+ zond api annotate apply --api <name> --seed-bodies --input /tmp/out.yaml # dry-run + diff
238
+ zond api annotate apply --api <name> --seed-bodies --input /tmp/out.yaml --yes # write
239
+ ```
240
+
241
+ YAML response format — a top-level list of entries, each one resource:
242
+
243
+ ```yaml
244
+ - resource: <name>
245
+ seed_body: # (--seed-bodies)
246
+ content_type: application/x-www-form-urlencoded
247
+ body:
248
+ amount: 1000
249
+ currency: usd
250
+ rationale: 'why this shape' # optional, helps future review
251
+ confidence: high # high|medium|low — optional
252
+ ```
253
+
254
+ Same shape for `readback_diff`, `idempotency`, `pagination`,
255
+ `lifecycle` blocks (see `zond-checks` for the per-aspect schemas).
256
+
257
+ **When to annotate**: run dump+apply BEFORE Phase 6 (stateful checks)
258
+ for any production API. Without annotation, stateful checks fall back
259
+ to defaults and miss API-specific quirks (custom pagination params,
260
+ non-standard lifecycle field names, write-only fields in create body).
261
+
262
+ **For Stripe-style form-encoded APIs**: `--seed-bodies` is the single
263
+ biggest win — it fixes `--seed` 400s for the bulk of resources.
264
+
265
+ ## Phase 3 — Generate tests
266
+
267
+ ```bash
268
+ zond generate apis/<name>/spec.json --output apis/<name>/tests [--tag <spec-tag>] [--uncovered-only]
269
+ zond check tests apis/<name>/tests
270
+ ```
271
+
272
+ `generate` fills bodies with `{{$randomString}}`. Format-strict APIs
273
+ reject many — that's a **test-fix**, not a backend bug. Pair with
274
+ Phase 1 fixtures + Phase 2 annotation.
275
+
276
+ If a CRUD chain you expected is missing, run
277
+ `zond generate <spec> --explain` — diagnostic table shows every POST
278
+ endpoint with verdict (no GET-by-id, non-`{id}` item param,
279
+ trailing-slash mismatch, …).
280
+
281
+ ## Phase 4 — Spec-lint (hygiene, separate workflow)
282
+
283
+ ```bash
284
+ zond lint --api <name> # 0 network requests
285
+ zond lint --api <name> --json | jq '.data.stats'
286
+ # Equivalent: `zond check spec --api <name>`
287
+ ```
288
+
289
+ ARV-255 (m-21 pivot): **spec-lint is hygiene, not security or
290
+ contract.** It runs separately from `zond audit` / `zond probe` /
291
+ `zond checks run` — those produce the security/reliability/contract
292
+ report, lint stays out of that pile.
293
+
294
+ Severity capped at **LOW / INFO** by design:
295
+ - LOW: real spec violations (format mismatch in example, missing
296
+ path-param format, response without schema).
297
+ - INFO: style and documentation gaps (additionalProperties missing,
298
+ naming, missing examples).
299
+
300
+ No HIGH/MEDIUM ever — static analysis has no runtime evidence, so the
301
+ severity matrix forbids escalation. `--strict` opts back into a
302
+ non-zero exit when any LOW lands.
303
+
304
+ ## Phase 5 — Run (sanity → smoke → CRUD)
47
305
 
48
- ## Phase 1 Discover
306
+ Wrap multi-run sweeps in a session so `/runs` shows one row:
49
307
 
50
308
  ```bash
51
- # Workspace + register API (idempotent)
52
- zond init --with-spec <spec> --name <name> [--base-url <url>]
53
- zond use <name>
309
+ zond session start --label "smoke + probes"
310
+ zond run apis/<name>/tests --tag sanity --report json # 5.1 sanity
311
+ zond run apis/<name>/tests --safe --report json # 5.2 smoke (GET-only)
312
+ zond run apis/<name>/tests --tag crud,setup --validate-schema --report json # 5.3 full CRUD
313
+ zond run apis/<name>/tests --tag positive --include 'path:^/emails' # 5.4 narrow
314
+ zond session end
315
+ ```
316
+
317
+ **Always pass `--validate-schema` for CRUD** — contract drift is
318
+ invisible without it. `schema_violation` failures are real backend bugs;
319
+ treat them like 5xx.
320
+
321
+ Rate limit: `zond run` defaults to an adaptive limiter (no-op until
322
+ `RateLimit-*` headers appear). Pass `--rate-limit <N>` for a hard cap;
323
+ `--sequential` for old binaries.
324
+
325
+ **Long runs**: `zond run` shows a periodic progress line on stderr when
326
+ stdout/stderr is a TTY (`zond: [42s] 234/10927 steps (2%), 230 req, ~30
327
+ req/s, ETA ~5m`) — overwrites itself in place. Suppressed by `--quiet`
328
+ and on non-interactive output. For huge probe-suites (e.g. GitHub spec
329
+ → ~10k probes under `--rate-limit 30` = 6+ min), this signals "alive,
330
+ not hung". `zond probe static` itself prints a scale-warning block with
331
+ ETA-at-rate-limit estimates and a `--max-per-endpoint K` sampling hint
332
+ when `totalProbes ≥ 2000`.
333
+
334
+ **Hard cap on requests**: pass `--max-requests N` to cap outgoing HTTP
335
+ calls across the whole run. Remaining steps short-circuit to `skip` with
336
+ `error: "max-requests-cap-reached"`. Each `retry_until` attempt counts as
337
+ one request. Useful for sampling huge probe runs (`--max-requests 500`)
338
+ and for CI time-boxing.
339
+
340
+ `--all`: fold every `apis/<name>/tests/` dir into one `runs.id` per
341
+ invocation (CI shape). CI context (commit_sha, branch, trigger=ci)
342
+ auto-stamped from env vars.
343
+
344
+ ### Spec-drift learning tail (`--learn`)
54
345
 
55
- # Endpoint discovery (do NOT read the raw spec)
56
- zond catalog <spec> --output apis/<name>/tests # writes .api-catalog.yaml
57
- zond describe <spec> --compact # quick overview
58
- zond guide <spec> --tests-dir apis/<name>/tests # uncovered + suggestions
346
+ After the initial CRUD run, **always** close with `--learn` /
347
+ `--learn-apply`. R09 measured a 28% 58% pass-coverage jump from this
348
+ phase alone.
349
+
350
+ ```bash
351
+ zond run apis/<name>/tests --learn # detect (read-only)
352
+ zond run apis/<name>/tests --learn-apply --learn-target test # rewrite expect.status
353
+ zond run apis/<name>/tests --learn-apply --learn-target drifts # allowlist in tolerated-drifts.yaml
59
354
  ```
60
355
 
61
- ## Phase 2 Generate
356
+ **Caveat `--learn-apply test` weakens assertions, not the code.**
357
+ Diff `apis/<name>/tests/*.yaml` + `tolerated-drifts.yaml` after every
358
+ apply; treat unexpected widenings as a review-blocker.
359
+
360
+ ## Phase 6 — Stateful checks (m-20)
361
+
362
+ After Phase 2 annotation is applied, run the cross-call invariants.
363
+ See `zond-checks` for per-check semantics.
62
364
 
63
365
  ```bash
64
- zond generate <spec> --output apis/<name>/tests # all
65
- zond generate <spec> --output apis/<name>/tests --tag <tag> # by spec tag
66
- zond generate <spec> --output apis/<name>/tests --uncovered-only
67
- zond validate apis/<name>/tests # YAML lint
366
+ zond checks run --api <name> --check stateful --report ndjson
68
367
  ```
69
368
 
70
- `generate` fills bodies with `{{$randomString}}`. Format-strict APIs will reject
71
- many of these that is a **test-fix**, not a backend bug. See phase 4 for the fix flow.
369
+ 5 m-20 stateful checks:
370
+ - `cross_call_references`POST→GET drift (uses readback_diff overlay)
371
+ - `idempotency_replay` — bit-identical replay on `Idempotency-Key`
372
+ - `pagination_invariants` — `?limit=N` + `?after=last_id` non-overlap
373
+ - `lifecycle_transitions` — declared state-machine validity
374
+ - `webhooks` (recipe-based, see `docs/recipes/webhook-receiver.md`)
375
+
376
+ **Iron rule**: don't run `--check stateful` without prior `api annotate`
377
+ review. Defaults catch the obvious; quirks need declared config.
72
378
 
73
- ## Phase 3Run (sanity smoke → full)
379
+ ## Phase 7Proactive bug hunting (probes)
74
380
 
75
381
  ```bash
76
- # 3.1 Sanity gate must pass before broad runs
77
- zond run apis/<name>/tests --tag sanity --json
382
+ zond probe static --api <name> # validation + methods (defaults)
383
+ zond probe mass-assignment --api <name> --emit-tests apis/<name>/probes/mass-assignment
384
+ zond probe security ssrf,crlf,open-redirect,prompt-injection --api <name> \
385
+ --emit-tests apis/<name>/probes/security
386
+ zond run apis/<name>/probes --report json
387
+ ```
78
388
 
79
- # 3.2 Smoke (GET-only)
80
- zond run apis/<name>/tests --safe --json
389
+ Flag: 5xx on null/empty/wrong-type body → missing validation. 2xx on
390
+ undeclared method contract drift. `is_admin: true` echoed in response
391
+ → HIGH from mass-assignment.
81
392
 
82
- # 3.3 Full CRUD only after setup-token capture works
83
- zond run apis/<name>/tests --tag crud,setup --json
393
+ **Body-FK auto-discovery**`mass-assignment` resolves required
394
+ `*_id`/`*_slug`/`*_uuid`/`*_key` fields by hitting sibling list endpoints.
395
+ If digest still says "unresolved body FKs:" — add to `.env.yaml` manually.
396
+
397
+ **Auto-path-FK** — same logic for path params (`{domain_id}`/`{webhook_id}`).
398
+ Cached per run. Pass `--no-discover` to opt out.
399
+
400
+ ### 7.1 — Manual mass-assignment catch-up
401
+
402
+ `mass-assignment` digest splits findings: HIGH / MED / LOW /
403
+ INCONCLUSIVE (no valid body — fixture problem) / INCONCLUSIVE-5XX
404
+ (baseline crashed — already in validation-probe). Sweep INCONCLUSIVE
405
+ with `--emit-template`:
406
+
407
+ ```bash
408
+ zond probe mass-assignment --api <name> --emit-template "POST:/<resource>" \
409
+ --output apis/<name>/probes/mass-assignment/<resource>.yaml
84
410
  ```
85
411
 
86
- ## Phase 4 Diagnose failures
412
+ Emits `create verify cleanup` chain pre-filled with classic vectors
413
+ (`is_admin`, `role`, `owner_id`). If a privileged field survives the
414
+ round-trip GET → **HIGH**, file via `report bundle --include case-study`.
415
+
416
+ ### 7.2 — Security probes
87
417
 
88
418
  ```bash
89
- zond db runs --limit 5 --json # find failed runId
90
- zond db diagnose <run-id> --json # full DiagnoseResult (grouped by root_cause)
91
- zond db run <id> --status 500 --json # filter results within a run
92
- zond db run <id> --method POST --json
93
- zond db compare <idA> <idB> --json # regression diff between runs
419
+ zond probe security ssrf,crlf --api <name> --dry-run # first run: which (endpoint, field)
420
+ zond probe security ssrf,crlf,open-redirect,prompt-injection --api <name>
94
421
  ```
95
422
 
96
- DiagnoseResult guide:
97
- - `agent_directive` literal next step. Do exactly that.
98
- - `recommended_action`:
99
- - `fix_test_logic` edit the YAML (assertion, generator, capture).
100
- - `report_backend_bug` STOP, report to user.
101
- - `update_expectation` → only if the user confirms the new contract is correct.
102
- - `root_cause` groups identical failures so one fix covers many cases.
423
+ Field autodetection: SSRF (`*_url`/`webhook`/`callback`/`format: uri`),
424
+ CRLF (`subject`/`*_prefix`/`name`), open-redirect (`redirect`/`next`).
425
+
426
+ Baseline-OK request first; if baseline 2xx `INCONCLUSIVE-BASELINE`,
427
+ attacks skipped (kills 5×404 noise on scope-locked endpoints).
428
+
429
+ Verdict: HIGH (5xx OR payload echoed in 2xx stored injection),
430
+ LOW (2xx, no echo — verify side-effects manually), OK (4xx).
431
+
432
+ **Cleanup is state-aware**: PUT/PATCH does `GET` → snapshot → attack →
433
+ restore. POST falls back to `DELETE` with eventual-consistency retry.
434
+ Restore failures → `## ⚠️ Cleanup failures` at top of digest.
435
+
436
+ CI exit: non-zero on HIGH > 0 OR `cleanup.error > 0`.
437
+
438
+ ### 7.3 — Post-probe hygiene
439
+
440
+ ```bash
441
+ zond prepare-fixtures --api <name> --refresh # drop stale FK ids
442
+ zond cleanup --orphans # retry DELETE for ~/.zond/orphans/
443
+ ```
103
444
 
104
- ### 4a. Fixing 4xx caused by stub generators
445
+ Skip only with `probe security --isolated` (isolated mode never attacks
446
+ seeded-fixture endpoints).
105
447
 
106
- When `recommended_action: fix_test_logic` and the body is rejected on format
107
- (400/422 with a field name and an "expected ..." message):
448
+ ## Phase 8 Coverage + spec drift
108
449
 
109
- 1. Read the failure body: `zond db run <id> --status 422 --json`.
110
- 2. **First pass** swap `{{$randomString}}` for the matching typed generator:
450
+ ```bash
451
+ zond coverage --api <name> --union session # default for an audit window
452
+ zond coverage --api <name> --union since:1h # last hour
453
+ zond coverage --api <name> --union tag:smoke # tag-filtered
454
+ zond coverage --api <name> --fail-on-coverage 50 # CI gate (use hit-coverage)
455
+ ```
111
456
 
112
- | API expects | Use |
113
- |---------------|----------------------|
114
- | email | `{{$randomEmail}}` |
115
- | hostname/FQDN | `{{$randomFqdn}}` |
116
- | URL | `{{$randomUrl}}` |
117
- | IPv4 | `{{$randomIpv4}}` |
118
- | UUID | `{{$uuid}}` |
119
- | integer | `{{$randomInt}}` |
120
- | ISO date | `{{$randomIsoDate}}` |
121
- | date | `{{$randomDate}}` |
122
- | person name | `{{$randomName}}` |
457
+ **Two metrics**: `pass-coverage` (covered2xx — strict, CI gate) vs
458
+ `hit-coverage` (any stored result, breadth proxy). Three-bucket JSON:
459
+ `covered2xx`, `coveredButNon2xx` (hit but failed — usually fixture gap,
460
+ not API bug), `unhit`.
123
461
 
124
- 3. **Second pass** if a typed generator still fails (regex too strict, enum,
125
- business constraint), drop to a hardcoded literal that satisfies the contract
126
- (e.g. `"https://example.com"`, `"info@example.com"`, `"2026-01-01T00:00:00Z"`).
127
- 4. **Dependent IDs** (`audience_id`, `topic_id`, anything that must reference an
128
- existing resource) — generators cannot help. Either capture the ID from a
129
- prior `create_*` step in the same suite, or move that creation into a
130
- `setup: true` suite and reference the captured variable.
462
+ **Quality signals to gate CI on** (not raw pass-coverage):
131
463
 
132
- ## Phase 5 Proactive bug hunting (probes)
464
+ | Signal | Command |
465
+ |---|---|
466
+ | Contract drift | `checks run --phase coverage` HIGH count == 0 |
467
+ | Stateful invariants | `checks run --check stateful` HIGH count == 0 |
468
+ | Auth / Authz / injection | `probe security` HIGH count == 0 |
469
+ | Mass-assignment | `probe mass-assignment` HIGH count == 0 |
470
+ | Response conformance | `run --validate-schema` `schema_violation` == 0 |
471
+ | Drift allowlist review | `git diff apis/<name>/tolerated-drifts.yaml` |
133
472
 
134
- Run on a passing API to surface latent bugs.
473
+ ## Phase 9 Share findings
135
474
 
136
475
  ```bash
137
- # Negative-input 5xx on malformed bodies/query/path
138
- zond probe-validation <spec> --output apis/<name>/probes/validation
139
- zond run apis/<name>/probes/validation --json
476
+ zond report export <run-id> # default: triage/<api>/run-<id>/
477
+ zond report bundle 135..142 -o triage/sweep/ # all artefacts (default): case-study + html + diagnose + index.md
478
+ zond report bundle <run-id> --include case-study,diagnose # subset only (drop html)
479
+ zond report bundle --session <id> -o triage/session/ # group by session
480
+ ```
481
+
482
+ Bodies > 8 KB truncated by default; `--no-body-cap` to keep full.
483
+ Existing files at `--output` auto-rotate to `<stem>-vN.<ext>`;
484
+ `--overwrite` to silence. **Always pass `--redact-identity` for
485
+ outbound sharing** (secret-redaction is on by default; identity slugs
486
+ need the extra flag).
487
+
488
+ Offer this proactively after a run surfaces `definitely_bug`
489
+ (5xx / schema violation / mass-assignment 2xx). Skip for `env_issue` /
490
+ `quirk`.
491
+
492
+ ## One-shot full audit
140
493
 
141
- # Undeclared HTTP methods 5xx or unexpected 2xx on methods not in spec
142
- zond probe-methods <spec> --output apis/<name>/probes/methods
143
- zond run apis/<name>/probes/methods --json
494
+ `zond audit --api <name>` runs the whole pipeline in one shot:
495
+ prepare-fixtures → generate → probe static → session-wrapped run on
496
+ tests+probes coverage → `audit-report.html`. Each stage prints
497
+ `==> Stage N/M: <name>`; stage failure doesn't stop the rest; final
498
+ exit 1 if any stage failed.
144
499
 
145
- # Triage
146
- zond db diagnose <run-id> --json
500
+ ```bash
501
+ zond audit --api <name> --dry-run # plan
502
+ zond audit --api <name> # minimal pipeline
503
+ zond audit --api <name> --seed # add prepare-fixtures --cascade --seed
504
+ zond audit --api <name> --with-mass-assignment --with-security
505
+ zond audit --api <name> --out reports/audit-<name>.html
147
506
  ```
148
507
 
149
- Typical findings:
150
- - **5xx on null / empty / oversized body** → missing input validation.
151
- - **5xx on wrong type** (string for int, etc.) → unguarded coercion.
152
- - **2xx on undeclared method** → contract drift (spec lies, or method is unprotected).
153
- - **5xx on missing required field** uncaught NPE on the server.
508
+ `generate` skipped via mtime when `tests/` is fresher than `spec.json`
509
+ (pass `--force` to override).
510
+
511
+ **Gotchas**:
512
+ - Wraps its own session closes any prior `session start` silently.
513
+ - Can exit 0 on failed stages (parse stdout `Warning: N failed`).
514
+ - `audit-report.html` path not echoed by default — pass `--out`.
515
+
516
+ When NOT to use audit: narrow asks ("fix run X", "why this endpoint
517
+ 500s") — walk phases.
518
+
519
+ ## Phase S — Single-flow scenarios
520
+
521
+ When the user wants to verify one specific flow (not breadth):
154
522
 
155
- Filter probe scope when an API is large:
156
523
  ```bash
157
- zond probe-validation <spec> --tag <spec-tag> --max-per-endpoint 20
158
- zond probe-methods <spec> --tag <spec-tag>
524
+ zond doctor --api <name> # confirm fixtures
525
+ # write apis/<name>/scenarios/<flow-slug>.yaml directly (no scaffold cmd)
526
+ zond session start --label "<short reason>"
527
+ zond run apis/<name>/scenarios/<flow>.yaml --report json
528
+ zond session end
529
+ ```
530
+
531
+ YAML grammar (zond runner format — NOT Postman / OpenAPI shape):
532
+
533
+ ```yaml
534
+ name: cancel-pending-order
535
+ tags: [scenario, orders]
536
+ base_url: "{{base_url}}"
537
+ headers: { Authorization: "Bearer {{auth_token}}" }
538
+ tests:
539
+ - name: create order
540
+ POST: /workspaces/{{ws_id}}/orders
541
+ json: { amount: 100, currency: USD }
542
+ expect:
543
+ status: 201
544
+ body:
545
+ id: { capture: order_id }
546
+ status: { equals: pending }
547
+ - name: cancel
548
+ POST: /orders/{{order_id}}/cancel
549
+ expect: { status: 200 }
550
+ - name: verify cancelled
551
+ GET: /orders/{{order_id}}
552
+ expect:
553
+ status: 200
554
+ body: { status: { equals: cancelled } }
555
+ - name: cleanup
556
+ DELETE: /orders/{{order_id}}
557
+ always: true # runs even on prior failure
558
+ expect: { status: [200, 204, 404] }
159
559
  ```
160
560
 
161
- ## Phase 6 — Coverage report & spec drift
561
+ Grammar:
562
+ - HTTP verb is a top-level key on the step (`GET:`, `POST:`, …).
563
+ - Body: `json: {...}` / `form: {...}` / `multipart:` / `text:`.
564
+ - Captures: `expect.body.<field>: { capture: <var> }`.
565
+ - Assertions: `equals`, `type`, `exists`, `matches`, `gt`/`lt`,
566
+ `length*`, `each`, `contains_item`, `set_equals`, `oneOf`.
567
+ - `expect.status`: number or array. `oneOf` / `anyOf` string forms NOT
568
+ supported — pass an array directly.
569
+ - `always: true` — runs on prior failure (cleanup).
570
+ - `skip_if: "{{var}} =="` — skip when fixture var unset.
571
+ - Generators: `{{$randomEmail}}`, `{{$uuid}}`, `{{$randomString}}`,
572
+ `{{$randomInt}}`, `{{$randomIsoDate}}`. Full list:
573
+ `zond reference random-helpers`.
574
+
575
+ Hand-crafted scenarios are for **bug repro / post-deploy smoke /
576
+ verifying a fix** — small focused work. For breadth, use Phase 3
577
+ (generate) and the full audit chain.
578
+
579
+ ## Diagnose failures
162
580
 
163
581
  ```bash
164
- zond coverage --api <name> --fail-on-coverage 80
165
- zond coverage --api <name> --run-id <id> # per-run breakdown
166
- zond sync <spec> --tests apis/<name>/tests # detect new/removed endpoints
582
+ zond db runs --limit 5 --json
583
+ zond db diagnose <run-id> --json # grouped by root_cause
584
+ zond db run <id> --status 500 --json
585
+ zond db compare <idA> <idB> --json # regression diff
167
586
  ```
168
587
 
588
+ `recommended_action` enum (closed):
589
+ - `report_backend_bug` — STOP, surface to user
590
+ - `fix_test_logic` — edit the YAML
591
+ - `fix_auth_config` / `fix_network_config` / `fix_env` — config issues
592
+ - `fix_spec` — edit OpenAPI (from `check spec`)
593
+ - `fix_fixture` — fill `.env.yaml` (from `prepare-fixtures` miss-* or
594
+ inconclusive mass-assignment baseline)
595
+ - `update_spec` — status-drift from `--learn`
596
+
597
+ `cascade` failures collapse under root cause — fix upstream, not
598
+ individually.
599
+
600
+ ### Fixing 4xx from stub generators (`fix_test_logic`)
601
+
602
+ When body rejected on format (400/422 + "expected ..."):
603
+
604
+ 1. Read failure: `zond db run <id> --status 422 --json`.
605
+ 2. **Fixture pack first** — if it's a FK id / verified resource /
606
+ constrained enum, add to `.env.yaml` (Phase 1).
607
+ 3. **Typed generator** — swap `{{$randomString}}` for matching format
608
+ helper.
609
+ 4. **Hardcoded literal** — when typed generators fail (regex too strict).
610
+ 5. **Runtime captures** — for resources the test creates (capture from
611
+ prior `create_*` step or `setup: true` suite). For pre-existing FKs,
612
+ prefer step 2.
613
+
614
+ For deep triage of a specific failing run → hand off to `zond-triage`.
615
+
169
616
  ## Auth / environments
170
617
 
171
- - Tokens go in `apis/<name>/.env.yaml` (auto-gitignored), referenced as `{{auth_token}}`.
172
- - Login-flow tokens: a `setup: true` suite captures into vars that propagate to later
173
- suites in the same run. See `apis/<name>/tests/setup.yaml` examples emitted by `zond generate`.
174
- - `zond run --env <name>` loads `.env.<name>.yaml` from the API directory.
618
+ - `apis/<name>/.env.yaml` is both auth and fixture pack — any key
619
+ interpolatable as `{{key}}`. Auto-gitignored.
620
+ - Login-flow tokens: `setup: true` suite captures vars that propagate
621
+ to later suites in the same run.
622
+ - `zond run --env <name>` loads `.env.<name>.yaml`. Discovery walks up
623
+ to workspace root; deeper files override shallower.
624
+
625
+ ## File lifecycle
626
+
627
+ Everything zond writes is tracked in `.zond/manifest.json` with
628
+ sha256-at-write. Files whose hash no longer matches are **skipped**
629
+ on clean (user edits stay safe). Re-running a generator overwrites
630
+ the entry — manual edits to generator-owned files are lost on
631
+ regenerate; promote them to a separate suite first.
632
+
633
+ ```bash
634
+ zond clean --api <name> # dry-run: lists what would be removed
635
+ zond clean --api <name> --force # delete (preserves probes/)
636
+ zond clean --api <name> --probes --force # also probe suites
637
+ zond clean --all --force # nuclear
638
+ ```
639
+
640
+ Triage outputs default to
641
+ `triage/<api>/<run-id>/<command>-<ts>.<ext>`. Don't pass `--output`
642
+ unless the user has a specific destination.
175
643
 
176
- ## When to hand off to `zond-scenarios`
644
+ ## When to hand off
177
645
 
178
- - The user asks for a multi-step user journey, business flow, or fixture creation
179
- through the API (login create cart checkout → cleanup).
180
- - A failing run's root cause requires a hand-written multi-step suite that
181
- `zond generate` cannot express.
646
+ - "deep depth-checks", "SARIF", "boundary coverage", "stateful
647
+ invariants details", "annotate aspect XYZ"`zond-checks`
648
+ - "what failed in last run", "why is it red", "case study from run X"
649
+ `zond-triage`
182
650
 
183
- For YAML format (assertions, generators, captures, `always: true` cleanup,
184
- `setup: true` propagation), see `ZOND.md` at the repo root or `zond run --help`.
651
+ If the user is mid-workflow in one of those, don't restart from here.