@kirrosh/zond 0.21.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 +758 -3
  2. package/README.md +78 -15
  3. package/package.json +17 -10
  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 +55 -6
  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 +192 -0
  24. package/src/cli/commands/coverage.ts +605 -132
  25. package/src/cli/commands/db.ts +180 -8
  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 -47
  31. package/src/cli/commands/init/agents-md.ts +61 -0
  32. package/src/cli/commands/init/bootstrap.ts +108 -0
  33. package/src/cli/commands/init/index.ts +244 -0
  34. package/src/cli/commands/init/skills.ts +98 -0
  35. package/src/cli/commands/init/templates/agents.md +77 -0
  36. package/src/cli/commands/init/templates/markdown.d.ts +4 -0
  37. package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
  38. package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
  39. package/src/cli/commands/init/templates/skills/zond.md +651 -0
  40. package/src/cli/commands/init/templates/zond-config.yml +14 -0
  41. package/src/cli/commands/prepare-fixtures.ts +135 -0
  42. package/src/cli/commands/probe/mass-assignment.ts +503 -0
  43. package/src/cli/commands/probe/security.ts +454 -0
  44. package/src/cli/commands/probe/static.ts +255 -0
  45. package/src/cli/commands/probe/webhooks.ts +161 -0
  46. package/src/cli/commands/probe.ts +459 -0
  47. package/src/cli/commands/reference.ts +87 -0
  48. package/src/cli/commands/refresh-api.ts +169 -0
  49. package/src/cli/commands/remove-api.ts +150 -0
  50. package/src/cli/commands/report-bundle.ts +318 -0
  51. package/src/cli/commands/report.ts +241 -0
  52. package/src/cli/commands/request.ts +379 -4
  53. package/src/cli/commands/run.ts +911 -33
  54. package/src/cli/commands/session.ts +244 -0
  55. package/src/cli/commands/use.ts +74 -0
  56. package/src/cli/index.ts +36 -607
  57. package/src/cli/json-envelope.ts +112 -3
  58. package/src/cli/json-schemas.ts +263 -0
  59. package/src/cli/program.ts +218 -0
  60. package/src/cli/resolve.ts +105 -0
  61. package/src/cli/status-filter.ts +124 -0
  62. package/src/cli/util/api-context.ts +85 -0
  63. package/src/cli/version.ts +8 -0
  64. package/src/core/anti-fp/bootstrap.ts +34 -0
  65. package/src/core/anti-fp/index.ts +33 -0
  66. package/src/core/anti-fp/registry.ts +44 -0
  67. package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
  68. package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
  69. package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
  70. package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
  71. package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
  72. package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
  73. package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
  74. package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
  75. package/src/core/anti-fp/types.ts +68 -0
  76. package/src/core/checks/checks/_crud-helpers.ts +133 -0
  77. package/src/core/checks/checks/_negative_mutator.ts +133 -0
  78. package/src/core/checks/checks/_readback-helpers.ts +133 -0
  79. package/src/core/checks/checks/content_type_conformance.ts +39 -0
  80. package/src/core/checks/checks/cross_call_references.ts +134 -0
  81. package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
  82. package/src/core/checks/checks/idempotency_replay.ts +246 -0
  83. package/src/core/checks/checks/ignored_auth.ts +211 -0
  84. package/src/core/checks/checks/index.ts +65 -0
  85. package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
  86. package/src/core/checks/checks/missing_required_header.ts +40 -0
  87. package/src/core/checks/checks/negative_data_rejection.ts +45 -0
  88. package/src/core/checks/checks/not_a_server_error.ts +27 -0
  89. package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
  90. package/src/core/checks/checks/pagination_invariants.ts +238 -0
  91. package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
  92. package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
  93. package/src/core/checks/checks/response_headers_conformance.ts +74 -0
  94. package/src/core/checks/checks/response_schema_conformance.ts +30 -0
  95. package/src/core/checks/checks/status_code_conformance.ts +61 -0
  96. package/src/core/checks/checks/unsupported_method.ts +63 -0
  97. package/src/core/checks/checks/use_after_free.ts +78 -0
  98. package/src/core/checks/index.ts +30 -0
  99. package/src/core/checks/mode.ts +79 -0
  100. package/src/core/checks/recommended-action.ts +64 -0
  101. package/src/core/checks/registry.ts +78 -0
  102. package/src/core/checks/runner.ts +874 -0
  103. package/src/core/checks/sarif.ts +230 -0
  104. package/src/core/checks/stateful.ts +121 -0
  105. package/src/core/checks/types.ts +189 -0
  106. package/src/core/classifier/recommended-action.ts +222 -0
  107. package/src/core/context/current.ts +51 -0
  108. package/src/core/context/session.ts +78 -0
  109. package/src/core/coverage/loader.ts +185 -0
  110. package/src/core/coverage/reasons.ts +300 -0
  111. package/src/core/diagnostics/db-analysis.ts +161 -12
  112. package/src/core/diagnostics/failure-class.ts +120 -0
  113. package/src/core/diagnostics/failure-hints.ts +212 -9
  114. package/src/core/diagnostics/spec-pointer.ts +99 -0
  115. package/src/core/diagnostics/suggested-fixes.ts +156 -0
  116. package/src/core/exporter/case-study/index.ts +270 -0
  117. package/src/core/exporter/curl.ts +40 -0
  118. package/src/core/exporter/exporter.ts +48 -0
  119. package/src/core/exporter/html-report/escape.ts +24 -0
  120. package/src/core/exporter/html-report/index.ts +479 -0
  121. package/src/core/exporter/html-report/script.ts +100 -0
  122. package/src/core/exporter/html-report/styles.ts +408 -0
  123. package/src/core/generator/chunker.ts +53 -15
  124. package/src/core/generator/coverage-phase.ts +0 -0
  125. package/src/core/generator/create-body.ts +89 -0
  126. package/src/core/generator/data-factory.ts +490 -33
  127. package/src/core/generator/describe.ts +1 -1
  128. package/src/core/generator/fixtures-builder.ts +325 -0
  129. package/src/core/generator/index.ts +7 -5
  130. package/src/core/generator/openapi-reader.ts +55 -3
  131. package/src/core/generator/path-param-disambig.ts +114 -0
  132. package/src/core/generator/resources-builder.ts +648 -0
  133. package/src/core/generator/schema-utils.ts +11 -3
  134. package/src/core/generator/serializer.ts +114 -15
  135. package/src/core/generator/suite-generator.ts +484 -77
  136. package/src/core/generator/types.ts +8 -0
  137. package/src/core/identity/identity-file.ts +129 -0
  138. package/src/core/lint/affects.ts +28 -0
  139. package/src/core/lint/config.ts +96 -0
  140. package/src/core/lint/format.ts +42 -0
  141. package/src/core/lint/index.ts +94 -0
  142. package/src/core/lint/reporter.ts +128 -0
  143. package/src/core/lint/rules/consistency.ts +158 -0
  144. package/src/core/lint/rules/heuristics.ts +97 -0
  145. package/src/core/lint/rules/strictness.ts +109 -0
  146. package/src/core/lint/types.ts +96 -0
  147. package/src/core/lint/walker.ts +248 -0
  148. package/src/core/meta/meta-store.ts +6 -73
  149. package/src/core/output/README.md +91 -0
  150. package/src/core/output/index.ts +13 -0
  151. package/src/core/output/run.ts +126 -0
  152. package/src/core/output/types.ts +129 -0
  153. package/src/core/parser/env-interpolation.ts +104 -0
  154. package/src/core/parser/filter.ts +57 -0
  155. package/src/core/parser/schema.ts +132 -5
  156. package/src/core/parser/types.ts +29 -2
  157. package/src/core/parser/variables.ts +0 -0
  158. package/src/core/parser/yaml-parser.ts +108 -13
  159. package/src/core/probe/bootstrap.ts +34 -0
  160. package/src/core/probe/dry-run-envelope.ts +57 -0
  161. package/src/core/probe/mass-assignment-probe-class.ts +198 -0
  162. package/src/core/probe/mass-assignment-probe.ts +1122 -0
  163. package/src/core/probe/mass-assignment-template.ts +212 -0
  164. package/src/core/probe/method-probe.ts +164 -0
  165. package/src/core/probe/method-shared.ts +69 -0
  166. package/src/core/probe/negative-probe.ts +691 -0
  167. package/src/core/probe/orphan-tracker.ts +188 -0
  168. package/src/core/probe/path-discovery.ts +440 -0
  169. package/src/core/probe/probe-harness.ts +120 -0
  170. package/src/core/probe/registry.ts +89 -0
  171. package/src/core/probe/runner.ts +136 -0
  172. package/src/core/probe/security-probe-class.ts +201 -0
  173. package/src/core/probe/security-probe.ts +1453 -0
  174. package/src/core/probe/shared.ts +505 -0
  175. package/src/core/probe/static-probe-class.ts +125 -0
  176. package/src/core/probe/types.ts +165 -0
  177. package/src/core/probe/verdict-aggregator.ts +33 -0
  178. package/src/core/probe/webhooks-probe.ts +284 -0
  179. package/src/core/reporter/console.ts +69 -4
  180. package/src/core/reporter/index.ts +2 -3
  181. package/src/core/reporter/json.ts +15 -2
  182. package/src/core/reporter/junit.ts +27 -12
  183. package/src/core/reporter/ndjson.ts +37 -0
  184. package/src/core/reporter/types.ts +3 -0
  185. package/src/core/runner/assertions.ts +62 -2
  186. package/src/core/runner/async-pool.ts +108 -0
  187. package/src/core/runner/auth-path.ts +8 -0
  188. package/src/core/runner/ci-context.ts +72 -0
  189. package/src/core/runner/executor.ts +391 -52
  190. package/src/core/runner/form-encode.ts +51 -0
  191. package/src/core/runner/http-client.ts +115 -7
  192. package/src/core/runner/learn-drift.ts +293 -0
  193. package/src/core/runner/preflight-vars.ts +149 -0
  194. package/src/core/runner/progress-tracker.ts +73 -0
  195. package/src/core/runner/rate-limiter.ts +203 -0
  196. package/src/core/runner/run-kind.ts +39 -0
  197. package/src/core/runner/schema-validator.ts +312 -0
  198. package/src/core/runner/send-request.ts +153 -20
  199. package/src/core/runner/types.ts +38 -0
  200. package/src/core/secrets/registry.ts +164 -0
  201. package/src/core/secrets/secrets-file.ts +115 -0
  202. package/src/core/selectors/operation-filter.ts +144 -0
  203. package/src/core/setup-api.ts +419 -17
  204. package/src/core/severity/category.ts +94 -0
  205. package/src/core/severity/index.ts +121 -0
  206. package/src/core/spec/layers.ts +154 -0
  207. package/src/core/util/format-eta.ts +21 -0
  208. package/src/core/utils.ts +5 -1
  209. package/src/core/workspace/config.ts +129 -0
  210. package/src/core/workspace/manifest.ts +283 -0
  211. package/src/core/workspace/output-rotation.ts +62 -0
  212. package/src/core/workspace/root.ts +94 -0
  213. package/src/core/workspace/triage-path.ts +87 -0
  214. package/src/db/lint-runs.ts +47 -0
  215. package/src/db/migrate.ts +126 -0
  216. package/src/db/migrations/0001_run_kind.sql +25 -0
  217. package/src/db/migrations/sql.d.ts +4 -0
  218. package/src/db/queries/collections.ts +133 -0
  219. package/src/db/queries/coverage.ts +9 -0
  220. package/src/db/queries/dashboard.ts +59 -0
  221. package/src/db/queries/results.ts +128 -0
  222. package/src/db/queries/runs.ts +235 -0
  223. package/src/db/queries/sessions.ts +42 -0
  224. package/src/db/queries/settings.ts +28 -0
  225. package/src/db/queries/types.ts +172 -0
  226. package/src/db/queries.ts +72 -802
  227. package/src/db/schema.ts +179 -48
  228. package/src/cli/commands/export.ts +0 -144
  229. package/src/cli/commands/guide.ts +0 -127
  230. package/src/cli/commands/init.ts +0 -57
  231. package/src/cli/commands/serve.ts +0 -81
  232. package/src/cli/commands/sync.ts +0 -269
  233. package/src/cli/commands/update.ts +0 -189
  234. package/src/cli/commands/validate.ts +0 -34
  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 -21
  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
@@ -0,0 +1,397 @@
1
+ ---
2
+ name: zond-checks
3
+ description: |
4
+ Schemathesis-style depth checks for an API — conformance + security
5
+ probes that go beyond YAML smoke tests. Use when the user asks for:
6
+ "deep audit", "find spec drift", "test edge cases", "boundary value
7
+ coverage", "find security bugs", "broken auth check", "use-after-free",
8
+ "SARIF for code scanning", "GitHub Code Scanning report", "stateful
9
+ invariants", "cross-call drift", "idempotency replay", "pagination
10
+ consistency", "lifecycle state machine". For a full generated test
11
+ pipeline + scenarios + audit chain, hand off to `zond`. For triage
12
+ of a failing run, use `zond-triage`.
13
+ allowed-tools: [Read, Bash(zond *), Bash(bunx zond *), Bash(jq *), Bash(sqlite3 *)]
14
+ ---
15
+
16
+ # zond-checks — Depth checks (conformance + security)
17
+
18
+ CLI-only skill. Use *after* a YAML smoke run passes — depth checks
19
+ exercise contract drift, malicious input rejection, broken auth, and
20
+ soft-deleted resource leaks against a live API.
21
+
22
+ The catalog is fixed and self-describing — list it before running so
23
+ the user sees what they'll get:
24
+
25
+ ```bash
26
+ zond checks list # registered checks (id, severity, expected)
27
+ zond checks list --json # same, machine-readable
28
+ ```
29
+
30
+ ## When to use
31
+
32
+ | User asks | Run |
33
+ |---|---|
34
+ | "deep audit", "find edge cases" | `zond checks run --api <name>` |
35
+ | "boundary value coverage" | `... --phase coverage` |
36
+ | "find security bugs", "broken auth" | `... --check ignored_auth,use_after_free,ensure_resource_availability` |
37
+ | "is GET returning what POST accepted?", "cross-call drift" | `... --check cross_call_references` (m-20) |
38
+ | "does the API honor Idempotency-Key?", "two-POST replay" | `... --check idempotency_replay` (m-20) |
39
+ | "are paginated lists consistent?", "duplicates across cursor pages" | `... --check pagination_invariants` (m-20) |
40
+ | "does cancel/archive land the resource in the declared state?", "state-machine" | `... --check lifecycle_transitions` (m-20) |
41
+ | "do captured webhook events match spec.webhooks shape?" | `zond probe webhooks --event-log events.jsonl` (m-20) — recipe: docs/recipes/webhook-receiver.md |
42
+ | "schemathesis-style strict mode" | `... --strict-405 --strict-401` (m-18) |
43
+ | "SARIF for GitHub Code Scanning" | `... --report sarif --output zond.sarif` |
44
+ | "stream findings to a pipeline" | `... --report ndjson \| jq -c '.'` |
45
+ | "scan large API faster" | `... --workers auto` (or `--workers 8`) |
46
+
47
+ ### Strict-mode флаги (m-18)
48
+
49
+ - **`--strict-405`** — `unsupported_method` принимает только 405 (вместо
50
+ pragmatic 401/403/404/405). Mirror schemathesis V4 default. Включай
51
+ когда target — backend, где политика «undeclared method → 405»
52
+ обязательна (RFC 9110 compliance).
53
+ - **`--strict-401`** — `ignored_auth` no-auth/bogus_auth требует ровно 401
54
+ (вместо любого 4xx). Включай для проверки точности auth-rejection policy.
55
+
56
+ Pragmatic режим (default) — реалистичный для production API'ев где gateway
57
+ часто возвращает 404 на undeclared method или 403 на missing auth.
58
+
59
+ ## Iron rules
60
+
61
+ - **NEVER hand-roll these checks in YAML.** The catalog encodes
62
+ schemathesis V4 semantics 1-to-1 — replicating them in YAML drifts
63
+ silently. `zond checks run` is the single source of truth.
64
+ - **NEVER run `--check stateful` without prior `api annotate` review.**
65
+ m-20 stateful checks (cross_call_references, idempotency_replay,
66
+ pagination_invariants, lifecycle_transitions) read `.api-resources.local.yaml`
67
+ for per-API quirks (custom pagination param, non-standard lifecycle
68
+ field, write-only ignore_fields). Defaults catch the obvious; running
69
+ on defaults alone misses API-specific drift. See **Phase pre-0** below.
70
+ - **NEVER ignore `--bootstrap-cleanup-failed`.** Stateful security
71
+ checks (`ignored_auth`, `use_after_free`, `ensure_resource_availability`)
72
+ produce false positives on stale data. If the previous run's
73
+ cleanup failed, pass the flag — they skip with a warning instead.
74
+ - **Auth headers are auto-derived from `--api <name>` `.env.yaml`**
75
+ (`auth_token` → `Authorization: Bearer …`, `api_key` → `X-API-Key`).
76
+ Add explicit ones with `--auth-header 'Name: value'` (repeatable).
77
+
78
+ ## Phase pre-0 — Annotation (mandatory for `--check stateful`)
79
+
80
+ > **Heads-up for `pagination_invariants`:** only cursor-style pagination
81
+ > is implemented in this milestone (`cursor`/`next`-token responses).
82
+ > APIs with `type: page` / `type: offset` (GitHub, Linear, Resend, …)
83
+ > are accepted by `annotate apply` but the check short-circuits with
84
+ > "type not implemented" — annotating page-based pagination has **no
85
+ > runtime effect**, skip the dump+apply step for those resources.
86
+
87
+ m-20 stateful checks rely on per-resource config in
88
+ `.api-resources.local.yaml` (overlay that survives `refresh-api`).
89
+ `zond api annotate` is the canonical authoring path: zond emits raw
90
+ spec slices, **agent (you) writes the YAML**, zond validates and
91
+ applies. **zond itself does NOT call any LLM** — agent is the LLM, zond
92
+ is the dumb-tool.
93
+
94
+ Six dump aspects (one per check class):
95
+
96
+ ```bash
97
+ zond api annotate dump --api <name> --seed-bodies > /tmp/seed.json
98
+ zond api annotate dump --api <name> --readback > /tmp/readback.json
99
+ zond api annotate dump --api <name> --idempotency > /tmp/idem.json
100
+ zond api annotate dump --api <name> --pagination > /tmp/pag.json
101
+ zond api annotate dump --api <name> --lifecycle > /tmp/life.json
102
+ zond api annotate dump --api <name> --resources > /tmp/orphans.json # optional: new CRUD resources from orphans
103
+ ```
104
+
105
+ Restrict scope with `--only r1,r2,r3` on any dump.
106
+
107
+ Agent reads each dump and writes a YAML response file (top-level list
108
+ of entries — see per-check schemas below for the block shape).
109
+ Optional `rationale` and `confidence: high|medium|low` per entry help
110
+ future review.
111
+
112
+ Apply — dry-run first (renders diff + conflicts), then `--yes` to write:
113
+
114
+ ```bash
115
+ zond api annotate apply --api <name> --readback --input /tmp/readback.yaml # dry-run
116
+ zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes # write
117
+ zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes --force # overwrite conflicts
118
+ ```
119
+
120
+ Conflicts: when an existing field already has a value, apply keeps
121
+ existing by default (renders `! field: (conflict — kept existing; pass
122
+ --yes to overwrite)`). Pass `--force` to overwrite.
123
+
124
+ **Recommended pre-stateful sweep on a new API:**
125
+
126
+ ```bash
127
+ zond api annotate dump --api <name> --seed-bodies > /tmp/seed.json # for prepare-fixtures --seed
128
+ zond api annotate dump --api <name> --readback > /tmp/readback.json # for cross_call_references
129
+ zond api annotate dump --api <name> --pagination > /tmp/pag.json # for pagination_invariants
130
+ zond api annotate dump --api <name> --lifecycle > /tmp/life.json # for lifecycle_transitions
131
+ zond api annotate dump --api <name> --idempotency > /tmp/idem.json # for idempotency_replay
132
+ # … agent generates YAML files for each …
133
+ zond api annotate apply --api <name> --seed-bodies --input /tmp/seed.yaml --yes
134
+ zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes
135
+ zond api annotate apply --api <name> --pagination --input /tmp/pag.yaml --yes
136
+ zond api annotate apply --api <name> --lifecycle --input /tmp/life.yaml --yes
137
+ zond api annotate apply --api <name> --idempotency --input /tmp/idem.yaml --yes
138
+ ```
139
+
140
+ Each block's YAML format is documented under the per-check section below.
141
+
142
+ ## Reading findings
143
+
144
+ Every finding carries a closed-enum `recommended_action` so the agent
145
+ can route without parsing free-form messages:
146
+
147
+ | `recommended_action` | What to do |
148
+ |---|---|
149
+ | `report_backend_bug` | Server returned 5xx / accepted invalid auth / leaked deleted resource. File a backend ticket; do not "fix" the test. |
150
+ | `fix_spec` | Server's behaviour is reasonable but spec doesn't predict it. Update `apis/<name>/spec.json` (or upstream OpenAPI) and `zond refresh-api`. |
151
+ | `tighten_validation` | Server accepted a body that violates the schema. Backend should reject earlier (400/422). |
152
+ | `add_required_header` | Spec marked a header `required: true`; server didn't enforce it. Either enforce it or relax the spec. |
153
+ | `fix_auth_config` | Auth-related failure. Check `apis/<name>/.env.yaml` (`auth_token`, `api_key`) — never log the value. |
154
+ | `fix_network_config` | Transport-level error (timeout / DNS / refused). Verify `base_url` and reachability before re-running. |
155
+ | `wontfix_known_limitation` | Known accepted gap. Don't retry, don't file a bug. |
156
+
157
+ Triage by `recommended_action` first, then by severity. HIGH/CRITICAL
158
+ gates exit-code 1; LOW/MEDIUM is informational.
159
+
160
+ ## Output formats
161
+
162
+ ```bash
163
+ zond checks run --api myapi --json # one envelope: findings[] + summary
164
+ zond checks run --api myapi --report sarif --output zond.sarif
165
+ # SARIF v2.1.0 for github/codeql-action/upload-sarif@v3
166
+ zond checks run --api myapi --report ndjson | jq -c '.' # streaming events: check_start, check_result, finding, summary
167
+ ```
168
+
169
+ NDJSON event schema is published at `docs/json-schema/ndjson-events.schema.json`
170
+ — pipe through `ajv validate` if you build a downstream consumer.
171
+
172
+ ## Scoping a run
173
+
174
+ Long runs feel sluggish on a 200-endpoint spec. Scope down before
175
+ broadening:
176
+
177
+ ```bash
178
+ # Filter operations (regex) — same grammar as `zond generate`
179
+ zond checks run --api myapi --include 'tag:billing,users' --exclude 'method:DELETE'
180
+
181
+ # Pick a vector
182
+ zond checks run --api myapi --mode positive # contract verification only
183
+ zond checks run --api myapi --mode negative # malicious-input probes only
184
+
185
+ # Pick a phase
186
+ zond checks run --api myapi --phase coverage # deterministic boundary values
187
+ zond checks run --api myapi --phase examples # default — one positive + one negative per op
188
+ ```
189
+
190
+ `--allow-x00` adds the NUL byte (`\x00`) to string boundaries during
191
+ coverage — off by default (some HTTP/JSON stacks panic on it).
192
+
193
+ ## Concurrency
194
+
195
+ ```bash
196
+ zond checks run --api myapi --workers auto # min(cpus, 8); usually right
197
+ zond checks run --api myapi --workers 16 # explicit; clamped to 64
198
+ zond checks run --api myapi --workers 8 --rate-limit 50
199
+ # 8 workers, global 50 RPS budget
200
+ ```
201
+
202
+ Workers parallelize at the *operation* level — cases inside one
203
+ operation always run sequentially (CRUD chain ordering must be
204
+ preserved). `--rate-limit auto` adapts from `RateLimit-*` response
205
+ headers (RFC 9568); use it on rate-limited APIs to avoid bursting.
206
+
207
+ ## "0 findings" doesn't always mean "all green"
208
+
209
+ The summary one-liner now ends with `(N check outcome(s) skipped: …)`
210
+ when probes can't validate (e.g. `response_schema_conformance: no JSON
211
+ Schema on this response branch ×2` when probes ran without auth and
212
+ got a 4xx that the spec only declares for 2xx). Treat skipped probes
213
+ as "not yet exercised", not "passed" — re-run with auth (or via
214
+ `zond run --validate-schema`) to actually cover those branches.
215
+
216
+ ## Exit codes
217
+
218
+ | Code | Meaning |
219
+ |---|---|
220
+ | 0 | No HIGH/CRITICAL findings (LOW/MEDIUM may be present). |
221
+ | 1 | At least one HIGH/CRITICAL finding — gate CI on this. |
222
+ | 2 | CLI-input error (bad flag value, unreachable spec, etc.). |
223
+
224
+ ## Common combos
225
+
226
+ ```bash
227
+ # Full conformance pass on staging, output SARIF for code scanning
228
+ zond checks run --api staging --report sarif --output zond.sarif --workers auto
229
+
230
+ # Just the security checks (stateful) with explicit auth
231
+ zond checks run --api prod \
232
+ --check ignored_auth,use_after_free,ensure_resource_availability \
233
+ --auth-header 'Authorization: Bearer $TOKEN'
234
+
235
+ # Coverage-phase boundary sweep, NDJSON pipe into a watcher
236
+ zond checks run --api dev --phase coverage --report ndjson | \
237
+ jq -c 'select(.type == "finding") | {check, op: .finding.operation, action: .finding.recommended_action}'
238
+
239
+ # Cross-call POST→GET drift only (m-20, single CRUD-chain check per resource)
240
+ zond checks run --api stripe --check cross_call_references
241
+ ```
242
+
243
+ ## Cross-call drift (m-20 ARV-169)
244
+
245
+ `cross_call_references` — POST resource → GET resource, diff write-shape
246
+ vs read-shape. Surfaces fields the server silently dropped:
247
+
248
+ - **state_not_persisted** — POST 2xx echoed the field, GET dropped it.
249
+ HIGH-signal: server lied about persisting state.
250
+ - **write_only** — POST accepted, GET dropped. Spec-declared write-only
251
+ fields (passwords, etc.) are auto-filtered.
252
+
253
+ Tunable per-resource in `apis/<name>/.api-resources.yaml` (или
254
+ `.api-resources.local.yaml` overlay):
255
+
256
+ ```yaml
257
+ resources:
258
+ - resource: customer
259
+ # … existing fields …
260
+ readback_diff:
261
+ ignore_fields: [metadata, livemode] # API-quirks, suppress
262
+ write_to_read_map:
263
+ tax_id_data: tax_ids # write-shape → read-shape
264
+ ```
265
+
266
+ Defaults already filter timestamps (`created_at`, `updated_at`), envelope
267
+ fields (`object`, `_links`), and ETag. Per-API quirks need a yaml line.
268
+ Authored either by hand or via the `zond api annotate dump --readback` →
269
+ agent → `apply` flow (see **Phase pre-0** above). zond emits the
270
+ write+read endpoint slice; agent decides `ignore_fields` /
271
+ `write_to_read_map` and writes the YAML; zond validates + writes to
272
+ `.api-resources.local.yaml`.
273
+
274
+ ## Idempotency replay (m-20 ARV-170)
275
+
276
+ `idempotency_replay` — two POSTs with the same `Idempotency-Key` header.
277
+ Server must (a) return the same resource id and (b) bit-identical response
278
+ bodies (modulo timestamps / request-id / etag).
279
+
280
+ - **duplicate_resource** — ids differ → server ignored the key. HIGH.
281
+ - **non_bit_identical** — same id but bodies drift on non-ignored fields
282
+ → replay isn't truly idempotent. Surfaced in the same HIGH finding via
283
+ `evidence.kind`.
284
+
285
+ Two ways to opt-in per resource:
286
+
287
+ 1. Spec declares `Idempotency-Key` as a header parameter on the create
288
+ endpoint → auto-detected, runs with defaults.
289
+ 2. `.api-resources.local.yaml` block (preferred — documents intent +
290
+ lets you tune the ignore list). Author via Phase pre-0
291
+ `annotate dump --idempotency` → agent → `apply`:
292
+
293
+ ```yaml
294
+ resources:
295
+ - resource: charge
296
+ # … existing fields …
297
+ idempotency:
298
+ header: Idempotency-Key # default; override for non-standard names
299
+ scope: endpoint # informational; `endpoint` | `global`
300
+ ignore_response_fields: # added on top of timestamp/request_id baseline
301
+ - retry_after
302
+ ```
303
+
304
+ Anti-FP: 429/409 on the 2nd POST → skip with cleanup. No DELETE on the
305
+ group → finding still fires, evidence carries `cleanup_warning`.
306
+
307
+ ## Pagination invariants (m-20 ARV-171)
308
+
309
+ `pagination_invariants` — fetch two consecutive cursor pages and assert
310
+ the contract holds:
311
+
312
+ - **duplicate_items** — an item id appears on both page A and page B
313
+ (off-by-one / cursor stops one short). HIGH-signal.
314
+ - **has_more_inconsistent** — page A said `has_more=true`, but page B is
315
+ empty and doesn't flip to `has_more=false`. Surfaces broken end-of-list
316
+ signalling.
317
+ - **partial_page_with_has_more** — page A returns fewer items than the
318
+ requested limit *yet* advertises `has_more=true`. Means the cursor is
319
+ prematurely truncating responses.
320
+
321
+ Cursor-style only in this milestone (Stripe / GitHub / Resend / Linear
322
+ pattern). `page` / `offset` / `token` declarations parse but the check
323
+ short-circuits with a "type not implemented" reason so the yaml block
324
+ stays a stable schema.
325
+
326
+ Auto-detect: if the list endpoint declares `starting_after` / `cursor` /
327
+ `after` / `page_token` as a query parameter, the check runs with
328
+ defaults (`cursor_field=id`, `items_field=data|items|results`,
329
+ `has_more_field=has_more`, `limit=2`).
330
+
331
+ Per-resource yaml override (author via Phase pre-0
332
+ `annotate dump --pagination` → agent → `apply`):
333
+
334
+ ```yaml
335
+ resources:
336
+ - resource: customer
337
+ # … existing fields …
338
+ pagination:
339
+ type: cursor # only cursor supported today
340
+ cursor_param: starting_after # Stripe-style; "after" / "cursor" / "page_token" also work
341
+ cursor_field: id # field on each item that feeds the next cursor
342
+ has_more_field: has_more # response field that flips on end-of-list
343
+ limit_param: limit # query param for page size
344
+ default_limit: 2 # probe page size — small on purpose
345
+ items_field: data # array container (falls back to items / results / value)
346
+ ```
347
+
348
+ ## Lifecycle transitions (m-20 ARV-172)
349
+
350
+ `lifecycle_transitions` — declare a state machine in
351
+ `.api-resources.yaml`, the check creates a resource and walks the
352
+ named actions, asserting:
353
+
354
+ - **undeclared_state** — observed state isn't in declared `states[]`.
355
+ - **wrong_expected_state** — action landed the resource in a state other
356
+ than its declared `expected_state`.
357
+ - **forbidden_transition** — observed (from, to) isn't in declared
358
+ transitions graph.
359
+ - **state_regression_on_replay** — invoking the action a second time
360
+ drifted state instead of staying idempotent.
361
+ - **double_action_5xx** — replay 5xx'd. Idempotent actions should 4xx
362
+ or 2xx, never crash.
363
+ - **action_rejected** — first-call non-2xx (server-side gating). Not a
364
+ contract bug per se, surfaced as INCONCLUSIVE-class info.
365
+
366
+ Manifest validation runs at yaml load (catches cycles, unreachable
367
+ states, missing terminal, actions referencing undeclared states)
368
+ before any HTTP call goes out.
369
+
370
+ Author via Phase pre-0 `annotate dump --lifecycle` → agent → `apply`.
371
+ The dump emits action-endpoint candidates (POST `/{resource}/{id}/cancel`,
372
+ PATCH `/{resource}/{id}/status` etc.); agent decides the state machine
373
+ graph.
374
+
375
+ ```yaml
376
+ resources:
377
+ - resource: subscription
378
+ # … existing fields …
379
+ lifecycle:
380
+ field: status
381
+ states: [pending, active, cancelled]
382
+ transitions:
383
+ - from: pending
384
+ to: [active, cancelled]
385
+ - from: active
386
+ to: [cancelled]
387
+ - from: cancelled
388
+ to: [] # terminal
389
+ actions:
390
+ cancel:
391
+ endpoint: POST /v1/subscriptions/{id}/cancel
392
+ expected_state: cancelled
393
+ ```
394
+
395
+ Action endpoints accept the `{id}` placeholder (replaced with the
396
+ captured create-id) or `{<idParam>}`. Body-less actions are the common
397
+ case; provide `body:` only for actions that demand a request payload.
@@ -0,0 +1,210 @@
1
+ ---
2
+ name: zond-triage
3
+ description: |
4
+ Last-run triage. Use when the user asks "что упало в последнем run", "почему
5
+ красное", "что с ralph-loop'ом сделать", "summary последнего прогона",
6
+ "explain failures", "что мне сейчас править". Reads `recommended_action`
7
+ enum from `zond db diagnose`, `zond check spec`, and `zond probe <class>` JSON
8
+ artifacts and emits an actionable summary grouped by next-step. No LLM
9
+ classification — every line is routed by the enum value emitted by zond.
10
+ allowed-tools: [Read, Bash(zond *), Bash(bunx zond *), Bash(jq *)]
11
+ ---
12
+
13
+ # zond-triage — last-run summary
14
+
15
+ Narrow skill: the user already has a finished run (or a recent
16
+ `probe <class>` / `check spec` artifact) and wants to know **what failed and
17
+ what to do next**. Sibling `zond` does the full audit + writes
18
+ scenarios; this one only reads.
19
+
20
+ ## Iron rules
21
+
22
+ - **Route on `recommended_action`, not on the message string.** Every zond
23
+ artifact stamps a closed enum: `report_backend_bug | fix_test_logic |
24
+ fix_auth_config | fix_network_config | fix_env | fix_spec |
25
+ fix_fixture | regenerate_suite | tighten_validation | add_required_header |
26
+ wontfix_known_limitation`. The last three (m-15 ARV-11) carry
27
+ depth-check findings from `zond checks run`; `regenerate_suite`
28
+ (m-16 ARV-42) means the failing YAML was emitted by `zond generate`
29
+ and editing it would be clobbered — re-run `zond generate --api
30
+ <name>` (or refine `.api-resources.yaml`) instead. Same triage rules
31
+ apply across the board: route by enum value. Group by enum, then
32
+ summarise. Never re-classify with prose heuristics.
33
+ - **One actionable line per group.** The agent-directive *is* the next
34
+ command — don't pad with "consider checking...".
35
+ - **`report_backend_bug` / 5xx → STOP, surface, do not edit `expect:`.**
36
+ Same iron rule as the parent `zond` skill.
37
+ - **`fix_env` overrides `fix_test_logic` at the suite level.** `db
38
+ diagnose` already does this collapse (TASK-70/98) — trust the field
39
+ it returns, don't merge again client-side.
40
+ - **Never read raw response bodies past 8 KB.** The diagnose envelope
41
+ truncates by default; pass `--no-body-cap` only if the user is
42
+ triaging body-shape bugs.
43
+ - **Никогда не выдавать абстрактные «проверьте логи» / «уточните у
44
+ команды».** Если в enum-группе нет конкретного действия — пометить
45
+ `<TODO: clarify>` и выйти, не маскировать пустоту.
46
+
47
+ ## Sources & enum routing
48
+
49
+ | Source | How to read | Emits `recommended_action` for |
50
+ |---|---|---|
51
+ | `zond db diagnose <run-id> --json` | per-failure under `data.failures[]` | every fail/error in the run |
52
+ | `zond check spec --api <name> --json` | `data.issues[]` | every lint Issue (always `fix_spec`) |
53
+ | `zond probe mass-assignment --json` | `data.verdicts[]` | per-endpoint verdicts (high/medium/inconclusive-5xx/inconclusive-baseline) |
54
+ | `zond probe security --json` | counts only — read the markdown digest | high / low findings (markdown table) |
55
+ | `zond probe static --json` | `data.files[]` (suite YAMLs to run) | findings surface only after `zond run` → routed via diagnose |
56
+
57
+ `probe security` does not expose findings in `--json`; treat its
58
+ markdown digest as the canonical source for that class and parse
59
+ HIGH/LOW rows by hand.
60
+
61
+ ## Phase 1 — locate the run
62
+
63
+ If the user did not name a run id, the default of `zond db diagnose`
64
+ already targets the most recent failing run (TASK-266). Skip straight to
65
+ Phase 2 unless you specifically need an older run:
66
+
67
+ ```bash
68
+ zond db runs --limit 5 --json # pick a non-default run id
69
+ ```
70
+
71
+ If `trigger=ci`, mention the CI build in the summary. If the user said
72
+ "после моего фикса", take the second-most-recent and pair with
73
+ `zond db compare <prev> <curr> --json` for a regression diff.
74
+
75
+ ## Phase 2 — pull the diagnose envelope
76
+
77
+ ```bash
78
+ zond db diagnose --json # last failing run (default — TASK-266)
79
+ zond db diagnose <run-id> --json # explicit run
80
+ zond db diagnose --latest --json # last run, even if it passed
81
+ ```
82
+
83
+ The shape (relevant fields only):
84
+
85
+ ```jsonc
86
+ {
87
+ "ok": true,
88
+ "data": {
89
+ "run_id": 42,
90
+ "summary": { "passed": 18, "failed": 7, "errored": 1 },
91
+ "env_issue": { "scope": "suite", "affected_suites": [...], "message": "..." },
92
+ "failures": [
93
+ {
94
+ "suite_name": "crud-projects",
95
+ "test_name": "create project",
96
+ "failure_type": "api_error",
97
+ "recommended_action": "report_backend_bug",
98
+ "request_method": "POST",
99
+ "request_url": "...",
100
+ "response_status": 500,
101
+ "hint": "...",
102
+ "schema_hint": "..."
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ ```
108
+
109
+ Bucket every failure by `recommended_action`. Display order (highest
110
+ priority first):
111
+
112
+ 1. `report_backend_bug` — 5xx / schema_violation / mass-assignment HIGH.
113
+ Bug. Surface excerpt; offer `zond report bundle --include case-study`.
114
+ 2. `fix_spec` — emitted only by `zond check spec`. Edit OpenAPI source,
115
+ then `zond refresh-api <name>`.
116
+ 3. `fix_auth_config` — 401/403 cluster. Check `auth_token` scope (or run
117
+ `zond doctor --api <name> --missing-only`).
118
+ 4. `fix_env` — env_issue cluster. Print `env_issue.message` verbatim;
119
+ point at `.env.yaml` (path is in the envelope).
120
+ 5. `fix_fixture` — `prepare-fixtures` miss-* or inconclusive-baseline
121
+ from mass-assignment. Run
122
+ `zond prepare-fixtures --api <name> --apply [--cascade [--seed]]`. If
123
+ the run was post-probe and IDs may be stale, prefer
124
+ `prepare-fixtures --refresh` (TASK-281) and follow with `zond cleanup
125
+ --orphans` (TASK-278) before retrying.
126
+ 6. `fix_network_config` — connect-refused / DNS / TLS. Check `base_url`
127
+ reachability; `--proxy` may be needed.
128
+ 7. `regenerate_suite` (ARV-42) — 4xx (400/422) on a generator-emitted
129
+ suite under `apis/<name>/tests/`. Editing the YAML is wrong: the
130
+ next `zond audit` overwrites it. Re-run `zond generate --api <name>`
131
+ so newer heuristics apply (e.g. ARV-38 default-string), or refine
132
+ `.api-resources.yaml` hints when the body shape can't be inferred.
133
+ 8. `fix_test_logic` — 4xx (400/422) on a manually-authored suite, or any
134
+ leftover assertion failure not absorbed by the buckets above. Phase 4a
135
+ of the `zond` skill: fixture pack first, typed generator second,
136
+ literal third.
137
+
138
+ Within each bucket, collapse by `(suite_name, response_status,
139
+ request_method, root_cause)` and report a count + 1-2 examples. Do
140
+ **not** dump every failure.
141
+
142
+ ## Phase 3 — reconcile spec / probe sources
143
+
144
+ Run only what the user's question implies — don't fan out blindly.
145
+
146
+ ```bash
147
+ # spec-level lint (only if user asked about spec drift / contract)
148
+ zond check spec --api <name> --json | jq '.data.issues | group_by(.rule)'
149
+
150
+ # mass-assignment digest (only if user mentioned mass-assign / privilege)
151
+ zond probe mass-assignment --api <name> --json
152
+ # verdicts with recommended_action != null are the actionable subset
153
+ ```
154
+
155
+ For `probe security`, the digest is the source. Read the file the user
156
+ named (or `apis/<name>/probes/security-digest.md`) and pull HIGH rows
157
+ plus the `## ⚠️ Cleanup failures` section if present.
158
+
159
+ ## Output template
160
+
161
+ Stay terse. Russian by default, mirror the user's language.
162
+
163
+ ```
164
+ Run #<id> · <ts> · session=<id?> · trigger=<ci|manual>
165
+ Pass <N> Fail <M> Error <K> Coverage <pct>%
166
+
167
+ ⛔ report_backend_bug ×<n>
168
+ · POST /v1/projects → 500 (×3) — TypeError: cannot read 'slug'
169
+ next: zond report bundle <id> --include case-study
170
+ 🛠 fix_test_logic ×<n>
171
+ · POST /v1/audiences → 422 expected uuid (×2)
172
+ next: replace {{$randomString}} with {{$uuid}} in audiences.yaml
173
+ 🔑 fix_auth_config ×<n>
174
+ · 4 suites all 401 — check auth_token scope
175
+ next: zond doctor --api <name> --missing-only
176
+ 🌐 fix_env ×<n>
177
+ · base_url unset (env_issue.scope=run)
178
+ next: edit apis/<name>/.env.yaml → base_url
179
+ 📥 fix_fixture ×<n>
180
+ · {{audience_id}} unresolved
181
+ next: zond prepare-fixtures --api <name> --apply --cascade
182
+ 📜 fix_spec ×<n> (from check spec)
183
+ · A2 missing operationId on POST /webhooks
184
+ next: edit spec.json → operationId, then zond refresh-api <name>
185
+ ```
186
+
187
+ Skip empty buckets. If `summary.failed + summary.errored == 0`, just
188
+ say "all green" and exit — don't invent work.
189
+
190
+ ## When to escalate
191
+
192
+ - **Mixed recommended_action inside one suite (>3 distinct enums):**
193
+ the run was probably aborted mid-setup. Check `env_issue` first; if
194
+ not set, the run hit a fatal early failure — `zond db run <id>
195
+ --status 500 --first-only --json` to find it.
196
+ - **Cleanup failures from `probe security`:** call out at the **top** —
197
+ this means probe mutated state it could not restore. Treat as
198
+ blocking; do not run more probes against the same env until the
199
+ user confirms manual cleanup.
200
+ - **`recommended_action` missing on a verdict you expected to see:**
201
+ TASK-294 stamps it on every issue/finding emitted post-Done. If a
202
+ field is missing, you're probably reading a pre-TASK-294 artifact —
203
+ re-run the source command, don't infer.
204
+
205
+ ## Hand-off back to `zond`
206
+
207
+ When the user wants to *act* on the summary (write a fix, file a
208
+ report, re-run the suite), hand off to the parent `zond` skill — it
209
+ owns the YAML edits, `zond report bundle` flow, and the re-run loop.
210
+ This skill stops at the summary.