@kirrosh/zond 0.23.0 → 0.26.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 (158) hide show
  1. package/CHANGELOG.md +164 -1
  2. package/README.md +8 -7
  3. package/package.json +2 -3
  4. package/src/CLAUDE.md +112 -0
  5. package/src/cli/commands/add-api.ts +19 -7
  6. package/src/cli/commands/api/annotate/index.ts +359 -4
  7. package/src/cli/commands/api/annotate/lifecycle.ts +1 -1
  8. package/src/cli/commands/api/annotate/overlay.ts +1 -1
  9. package/src/cli/commands/api/annotate/pagination.ts +10 -6
  10. package/src/cli/commands/api/annotate/prompts.ts +39 -2
  11. package/src/cli/commands/audit.ts +360 -54
  12. package/src/cli/commands/check.ts +15 -2
  13. package/src/cli/commands/checks.ts +352 -36
  14. package/src/cli/commands/cleanup.ts +4 -30
  15. package/src/cli/commands/coverage.ts +275 -57
  16. package/src/cli/commands/db.ts +311 -8
  17. package/src/cli/commands/discover.ts +281 -161
  18. package/src/cli/commands/doctor.ts +57 -3
  19. package/src/cli/commands/fixtures.ts +1 -1
  20. package/src/cli/commands/generate.ts +24 -7
  21. package/src/cli/commands/init/bootstrap.ts +4 -1
  22. package/src/cli/commands/init/index.ts +2 -2
  23. package/src/cli/commands/init/skills.ts +43 -0
  24. package/src/cli/commands/init/templates/agents.md +12 -3
  25. package/src/cli/commands/init/templates/skills/warm-up-target.md +122 -0
  26. package/src/cli/commands/init/templates/skills/zond-checks.md +268 -44
  27. package/src/cli/commands/init/templates/skills/zond-seed.md +114 -0
  28. package/src/cli/commands/init/templates/skills/zond-triage.md +88 -26
  29. package/src/cli/commands/init/templates/skills/zond.md +274 -64
  30. package/src/cli/commands/init/templates/zond-config.yml +1 -1
  31. package/src/cli/commands/prepare-fixtures.ts +14 -52
  32. package/src/cli/commands/probe/_seed-bodies.ts +52 -0
  33. package/src/cli/commands/probe/mass-assignment.ts +101 -10
  34. package/src/cli/commands/probe/security.ts +95 -12
  35. package/src/cli/commands/probe/webhooks.ts +2 -0
  36. package/src/cli/commands/probe.ts +87 -11
  37. package/src/cli/commands/refresh-api.ts +59 -1
  38. package/src/cli/commands/report-bundle.ts +3 -11
  39. package/src/cli/commands/request.ts +116 -0
  40. package/src/cli/commands/run.ts +33 -5
  41. package/src/cli/commands/schema-from-runs.ts +128 -0
  42. package/src/cli/commands/secrets.ts +133 -0
  43. package/src/cli/json-envelope.ts +0 -20
  44. package/src/cli/json-schemas.ts +51 -0
  45. package/src/cli/output.ts +17 -1
  46. package/src/cli/program.ts +5 -4
  47. package/src/cli/safe-live.ts +24 -0
  48. package/src/cli/status-filter.ts +0 -10
  49. package/src/core/audit/persist.ts +183 -0
  50. package/src/core/checks/budget.ts +59 -0
  51. package/src/core/checks/checks/cross_call_references.ts +17 -4
  52. package/src/core/checks/checks/cursor_boundary_fuzzing.ts +219 -0
  53. package/src/core/checks/checks/idempotency_replay.ts +1 -5
  54. package/src/core/checks/checks/ignored_auth.ts +44 -1
  55. package/src/core/checks/checks/index.ts +3 -0
  56. package/src/core/checks/checks/lifecycle_transitions.ts +169 -26
  57. package/src/core/checks/checks/negative_data_rejection.ts +119 -16
  58. package/src/core/checks/checks/not_a_server_error.ts +8 -0
  59. package/src/core/checks/checks/open_cors_on_sensitive.ts +47 -18
  60. package/src/core/checks/checks/pagination_invariants.ts +298 -117
  61. package/src/core/checks/checks/positive_data_acceptance.ts +1 -4
  62. package/src/core/checks/checks/status_code_conformance.ts +78 -7
  63. package/src/core/checks/mode.ts +3 -0
  64. package/src/core/checks/recommended-action.ts +5 -1
  65. package/src/core/checks/runner.ts +614 -27
  66. package/src/core/checks/spec-findings.ts +308 -0
  67. package/src/core/checks/types.ts +117 -1
  68. package/src/core/checks/zond-extensions.ts +73 -0
  69. package/src/core/classifier/recommended-action.ts +35 -6
  70. package/src/core/coverage/loader.ts +31 -0
  71. package/src/core/diagnostics/db-analysis.ts +200 -106
  72. package/src/core/diagnostics/failure-class.ts +21 -1
  73. package/src/core/diagnostics/failure-hints.ts +4 -208
  74. package/src/core/diagnostics/suggested-fixes.ts +2 -3
  75. package/src/core/generator/chunker.ts +1 -8
  76. package/src/core/generator/data-factory.ts +199 -61
  77. package/src/core/generator/fixtures-builder.ts +38 -31
  78. package/src/core/generator/index.ts +0 -2
  79. package/src/core/generator/openapi-reader.ts +98 -4
  80. package/src/core/generator/path-param-disambig.ts +30 -4
  81. package/src/core/generator/resources-builder.ts +276 -26
  82. package/src/core/generator/schema-utils.ts +22 -0
  83. package/src/core/generator/suite-generator.ts +168 -15
  84. package/src/core/generator/types.ts +6 -0
  85. package/src/core/identity/identity-file.ts +0 -0
  86. package/src/core/output/README.md +11 -29
  87. package/src/core/output/index.ts +1 -1
  88. package/src/core/output/run.ts +0 -35
  89. package/src/core/output/types.ts +0 -7
  90. package/src/core/parser/dynamic-values.ts +160 -0
  91. package/src/core/parser/variables.ts +0 -0
  92. package/src/core/probe/dry-run-envelope.ts +4 -0
  93. package/src/core/probe/mass-assignment/classify.ts +175 -0
  94. package/src/core/probe/mass-assignment/cleanup.ts +52 -0
  95. package/src/core/probe/mass-assignment/digest.ts +114 -0
  96. package/src/core/probe/mass-assignment/orchestrator.ts +459 -0
  97. package/src/core/probe/mass-assignment/regression.ts +141 -0
  98. package/src/core/probe/mass-assignment/suspects.ts +92 -0
  99. package/src/core/probe/mass-assignment/types.ts +135 -0
  100. package/src/core/probe/mass-assignment-probe.ts +23 -1118
  101. package/src/core/probe/mass-assignment-template.ts +32 -4
  102. package/src/core/probe/path-discovery.ts +3 -4
  103. package/src/core/probe/probe-harness.ts +21 -22
  104. package/src/core/probe/security/baseline.ts +174 -0
  105. package/src/core/probe/security/classify.ts +341 -0
  106. package/src/core/probe/security/cleanup.ts +125 -0
  107. package/src/core/probe/security/detectors.ts +71 -0
  108. package/src/core/probe/security/digest.ts +104 -0
  109. package/src/core/probe/security/orchestrator.ts +398 -0
  110. package/src/core/probe/security/regression.ts +103 -0
  111. package/src/core/probe/security/types.ts +151 -0
  112. package/src/core/probe/security-probe-class.ts +8 -2
  113. package/src/core/probe/security-probe.ts +28 -1449
  114. package/src/core/probe/shared.ts +26 -0
  115. package/src/core/probe/webhooks-probe.ts +5 -7
  116. package/src/core/runner/assertions.ts +1 -1
  117. package/src/core/runner/executor.ts +3 -18
  118. package/src/core/runner/form-encode.ts +8 -18
  119. package/src/core/runner/http-client.ts +38 -1
  120. package/src/core/runner/preflight-vars.ts +19 -15
  121. package/src/core/runner/rate-limiter.ts +11 -29
  122. package/src/core/runner/run-kind.ts +7 -1
  123. package/src/core/runner/schema-validator.ts +2 -6
  124. package/src/core/runner/send-request.ts +11 -6
  125. package/src/core/runner/types.ts +6 -0
  126. package/src/core/setup-api.ts +53 -15
  127. package/src/core/severity/index.ts +0 -63
  128. package/src/core/spec/infer-schema.ts +102 -0
  129. package/src/core/spec/merge-specs.ts +156 -0
  130. package/src/core/spec/schema-from-runs.ts +117 -0
  131. package/src/core/spec/schema-overlay.ts +130 -0
  132. package/src/core/util/ajv.ts +13 -0
  133. package/src/core/util/headers.ts +9 -0
  134. package/src/core/util/url.ts +24 -0
  135. package/src/core/workspace/fixture-gap-report.ts +84 -0
  136. package/src/core/workspace/fixture-gaps.ts +71 -0
  137. package/src/core/workspace/root.ts +13 -11
  138. package/src/db/migrate.ts +2 -0
  139. package/src/db/migrations/0002_run_kind_request.sql +59 -0
  140. package/src/db/queries/collections.ts +2 -2
  141. package/src/db/queries/results.ts +88 -0
  142. package/src/db/queries/runs.ts +56 -2
  143. package/src/db/queries.ts +3 -0
  144. package/src/db/schema.ts +7 -7
  145. package/src/cli/commands/bootstrap.ts +0 -710
  146. package/src/core/anti-fp/bootstrap.ts +0 -34
  147. package/src/core/anti-fp/index.ts +0 -33
  148. package/src/core/anti-fp/registry.ts +0 -44
  149. package/src/core/anti-fp/rules/baseline-echo.ts +0 -74
  150. package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +0 -52
  151. package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +0 -38
  152. package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +0 -35
  153. package/src/core/anti-fp/rules/schemathesis/index.ts +0 -24
  154. package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +0 -53
  155. package/src/core/anti-fp/rules/subscription-gated/index.ts +0 -11
  156. package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +0 -75
  157. package/src/core/anti-fp/types.ts +0 -68
  158. package/src/core/generator/create-body.ts +0 -89
package/CHANGELOG.md CHANGED
@@ -4,7 +4,170 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- (Emptynext development cycle.)
7
+ ## [0.26.0] 2026-07-09
8
+
9
+ Deterministic gap-fills surfaced by a docgen-core-service audit — all pass the
10
+ litmus test (same input → same output, no severity/FP/blame judgment). Plus an
11
+ m-25 distribution cleanup that collapses the workspace layout to one convention.
12
+
13
+ ### Added
14
+ - **Multi-spec merge (ARV-375):** `zond add api <name> --spec a --spec b …`
15
+ unions N specs into one audit target. Deterministic last-wins on path /
16
+ component collisions, surfaced as warnings (path collisions + same-name /
17
+ different-shape schema collisions). Core in `src/core/spec/merge-specs.ts`.
18
+ - **`zond secrets set <key> <value>` (ARV-377):** writes `apis/<name>/.secrets.yaml`
19
+ with a `.bak` backup, never echoes the value, and auto-strips a pasted
20
+ `Bearer ` prefix (`--literal` keeps it verbatim). Closes the "hand-rolled
21
+ python to rotate an expired token" gap and ARV-367/AC3.
22
+ - **`zond db diagnose --union <spec>` (ARV-380):** aggregate
23
+ `by_recommended_action` (+ summary counts, deduped examples) across a run
24
+ set, reusing coverage's `--union` vocabulary (`session` / `since:<dur>` /
25
+ `tag:<name>` / `runs:<ids>`). `--api` scopes `since:`/`tag:` to a collection.
26
+ - **Coverage deprecated flags (ARV-379):** `coverage --json` now carries a
27
+ per-entry `deprecated` boolean on the `*Endpoints` bucket arrays plus a
28
+ top-level `deprecatedEndpoints` list, so a consumer can split "real gap"
29
+ from "deprecated, skip by design" without re-reading spec.json.
30
+
31
+ ### Fixed
32
+ - **`db diagnose --union` version follow-up + candidate-surfacing (ARV-381/382):**
33
+ disambig's parent-walk now skips version segments (`/v30/{code}`), aligning
34
+ with `owningCollectionForPathParam` which already strips them — resolves the
35
+ `_v30_code` miss-no-list class. And when prepare-fixtures can't confidently
36
+ derive an owner list, the item carries `candidates[]` — plausible GET/list
37
+ endpoints ranked by proximity (deprecated ones marked) — instead of
38
+ dead-ending; zond surfaces the evidence, the agent picks the value. On
39
+ docgen-core-merged: `miss-no-list` 40 → 22 total, 9 of those now carry
40
+ candidates, the rest are honest dead-ends (no listable source in the spec).
41
+ - **Resource graph misses `/list`-style owners (ARV-376):** `resolveOwnerListPaths`
42
+ now links `/list` (and `/search`, `/find`) endpoints to their sibling
43
+ `/{code}` and `/byid/{id}` params; the `byid` accessor marker no longer
44
+ collapses every read-by-id param into one global `byid_id`; a malformed spec
45
+ whose path template (`/byid/{id}`) disagrees with the declared param name
46
+ (`byid_id`) is reconciled to the template; secondary lookup keys (`{code}`)
47
+ surface as candidates. On the docgen-core-merged spec this cut
48
+ prepare-fixtures `miss-no-list` from 40 → 30.
49
+ - **`fixtures add` batch (ARV-378):** confirmed `fixtures add <pairs...>`
50
+ already applies N `key=value` pairs in one call (one write + one `.bak`) —
51
+ no shell loop needed.
52
+
53
+ ### Removed
54
+ - **Flat `zond.db` workspace layout (m-25):** dropped the legacy root-level
55
+ `zond.db` marker and its implicit default-DB resolution. The DB now lives
56
+ only at `.zond/zond.db`; `zond.config.yml` / `.zond/` / `apis/` remain the
57
+ workspace markers. Migrate an old workspace with `mv zond.db .zond/` (or
58
+ re-`zond init`); an explicit `--db <path>` still opens any file. Removes the
59
+ dual-layout confusion that let a root `zond.db` and `.zond/zond.db` coexist.
60
+ - **Dead `benchmarks/` references (m-25):** pruned the never-committed
61
+ `benchmarks/**` entries from `knip.json` and the dead `bench:api` script from
62
+ `package.json` (`knip` is now clean).
63
+
64
+ ## [0.25.0] — 2026-07-07
65
+
66
+ m-24: **rebuild around the agent.** zond becomes a set of deterministic
67
+ dumb tools — it sends requests, validates schemas, stores and diffs runs,
68
+ and reports gaps. The autonomous heuristic layer that produced most of the
69
+ 0.24 bug-stream (auto-discovery/seed/cascade, annotate-auto guess-engine,
70
+ severity calibrators, anti-FP suppression) is removed; the agent now owns
71
+ every judgment call (severity, spec-vs-backend blame, "is this a false
72
+ positive?", which value fills a fixture). The deterministic core —
73
+ send → validate → store → diff — survives intact. A litmus test in
74
+ `src/CLAUDE.md` keeps the heuristic layer from creeping back one
75
+ "reasonable" fix at a time: same input → same output, or it's the agent's.
76
+
77
+ ### Removed (heuristic layer)
78
+ - **Autonomous fixture seed/cascade engine gone (ARV-336):** `bootstrap.ts`
79
+ + `create-body.ts` deleted. Live-POST resource creation guessed bodies and
80
+ cascaded parent→child (1% success on Stripe). prepare-fixtures is now
81
+ single-pass verify + gap-report; the agent (or user) creates resources.
82
+ - **Severity calibrators + anti-FP suppression gate removed (ARV-337 Cut A):**
83
+ severity is agent judgment; anti-FP `applyAntiFp` no longer gates emission —
84
+ the rule reasons survive as raw evidence, not as a suppress-or-emit verdict.
85
+ - **annotate-auto guess-engine removed (ARV-337 Cut B):** `inferSeedBody`
86
+ fabricated bodies from format/name fallbacks and self-ranked confidence.
87
+ annotate now dumps the spec slice; the agent authors the YAML, zond
88
+ validates and merges.
89
+
90
+ ### Agent-facing artifacts & new mechanics
91
+ - **YAML run summary (ARV-338):** `zond db diagnose` drops prose hints (keeps
92
+ the mechanical counts/grouping spine); `db … --report yaml` emits
93
+ agent-friendly output.
94
+ - **Field-level run diff (ARV-339):** `zond db compare` now diffs response
95
+ body/schema per field, not just status.
96
+ - **prepare-fixtures gap report (ARV-349/350):** undefined vars + unseeded
97
+ capture-chain roots are reported deterministically — no auto-seed.
98
+ - **Agent-orchestrated auto-seed (ARV-355):** `zond request --capture` +
99
+ `zond-seed` skill — the agent reasons about creation order, zond executes.
100
+ - **Ambiguous-id guard (ARV-334):** the hint-less fixture harvest reports
101
+ `miss-ambiguous-id` (naming the candidate string field) instead of silently
102
+ putting a numeric `id` into a string slot like `{owner}` — zond flags the
103
+ ambiguity, the agent/annotation picks the field.
104
+ - **generate preserves hand-edits (ARV-361):** suite files whose auto-gen
105
+ header was removed are treated as agent-owned and preserved on regenerate;
106
+ `--force` overwrites them (was a documented no-op).
107
+
108
+ ### Checks, probes & safety
109
+ - **`--safe`/`--live` parity (ARV-299)** across `checks run` and `probe`.
110
+ - **stateful scope (ARV-325):** `--check stateful` expands to the
111
+ state-machine set only.
112
+ - **cascade fast-fail (ARV-326):** prepare-fixtures aborts early on a
113
+ dead/scoped-wrong token instead of grinding through futile attempts.
114
+ - **input-triggered 5xx caught (ARV-340/341):** self-generated body rejects
115
+ route to `fix_spec`, not `report_backend_bug`.
116
+ - **child-exclude wired (ARV-330):** the last-attempt history lookup uses the
117
+ all-run-kinds query + child-exclude pattern so probe sub-resource POSTs
118
+ (`/v1/accounts/{id}/reject`) don't starve the real create-path window.
119
+ - **checks reporting fixes (ARV-322/323/328):** suppressed-count accuracy,
120
+ SIGTERM ndjson counting, throttled stderr progress on long runs.
121
+
122
+ ### Sweeps & infrastructure
123
+ - **Bounded/resumable sweeps (ARV-342):** `--skip-ops` / `--max-ops` op-window.
124
+ - **DB retention (ARV-266):** `zond db prune` + `zond db stats`, per-run-kind
125
+ cutoffs, VACUUM after delete.
126
+ - **Schema from observed runs (ARV-175/176):** `schema-from-runs` command +
127
+ `refresh-api --merge-schema` — union/required-intersection from real
128
+ responses, not guessing.
129
+
130
+ ### Report, triage & audit-run cleanup
131
+ - Report-noise, severity and UX cleanup (ARV-343/344/345/346/348/351/353);
132
+ compare scope tag, partial summary on SIGTERM, triage depth aggregate
133
+ (ARV-352/354).
134
+ - Empty-dir `zond run --output` writes a `0 tests` envelope instead of no
135
+ file (ARV-357); cross-phase double-emit documented for triage (ARV-358).
136
+ - Workspace-leak fix (env-independent audit paths) + self-compare guard
137
+ (ARV-359/360), from a petstore audit-run.
138
+
139
+ ## [0.24.0] — 2026-07-03
140
+
141
+ m-22 validation sprint. No new milestone surface — this release hardens the
142
+ 0.23.0 depth-checks/probe/audit stack against real APIs. ~40 fixes (ARV-260..332)
143
+ were found by running `zond audit`/`zond-scan` live against Stripe, GitHub,
144
+ Resend and docgen-core, not by unit tests.
145
+
146
+ ### Safety & contracts
147
+ - **Safe-mode leak closed (ARV-332):** `checks run --check stateful --include
148
+ method:GET` no longer fires POST create-chains — CRUD groups are built from
149
+ the filtered op set, so `ensure_resource_availability`/`use_after_free`
150
+ self-skip when no write is in scope. Critical for scanning APIs you don't own.
151
+ - Exit-code contract hardened (ARV-303/307/308/309/310): audit judges stages on
152
+ `envelope.ok`, not raw exit code; budget-exhaust distinguished from network error.
153
+ - `open_cors_on_sensitive` HIGH only on 2xx + ambient (cookie) credential
154
+ (ARV-312/316) — kills a class of false HIGHs.
155
+
156
+ ### Severity calibration
157
+ - Probe-side severity calibrator (ARV-283/300): `SecuritySeverity ↔ Severity`
158
+ adapter with sentinel passthrough; wired into security probe. mass-assignment/
159
+ static/webhooks tracked in ARV-311.
160
+
161
+ ### Fixture / seed pipeline
162
+ - FK-aware body builder wires reference fields to fixtures (ARV-45).
163
+ - Topological seed order + body-FK deferral (ARV-324/327); gap-report flags
164
+ unfillable complex root resources instead of silently skipping (ARV-329/330).
165
+
166
+ ### Ops & workflow
167
+ - `ZOND_WORKSPACE` honored; `zond-audit` workflow added for reproducible runs.
168
+ - ndjson reporter stability (ARV-314/318/320/322); path-casing canonicalized
169
+ before workspace-root walk (ARV-315).
170
+ - Repo-wide ponytail cleanup: dead code removed, probe monoliths deduped.
8
171
 
9
172
  ## [0.23.0] — 2026-05-15
10
173
 
package/README.md CHANGED
@@ -21,12 +21,12 @@ Bootstrap a workspace, register your first API, then fill its fixtures:
21
21
  zond init # bootstrap workspace (no fixture changes)
22
22
  zond add api my-api --spec ./openapi.json # register: copies spec.json + emits manifest
23
23
  zond doctor --api my-api --missing-only # gap report: which vars are UNSET
24
- zond prepare-fixtures --api my-api --apply [--seed] # fill apis/my-api/.env.yaml from live API
24
+ zond prepare-fixtures --api my-api # gap report: verify fixtures + which FK vars need a value
25
25
  ```
26
26
 
27
- For path-FK ids that auto-discover/--seed can't reach (vendor-dashboard
28
- ids like `cus_*`, GitHub PR numbers, Sentry issue ids), use the manual
29
- helpers (ARV-195):
27
+ `prepare-fixtures` **reports** gaps it never harvests a value (which
28
+ record/field fills a path slot is your call). Fill each gap with the
29
+ manual helpers (ARV-195):
30
30
 
31
31
  ```bash
32
32
  zond fixtures add --api my-api customer_id=cus_123 --validate --apply
@@ -46,9 +46,10 @@ Each registered API gets four files in `apis/<name>/`:
46
46
  - `.api-resources.yaml` — CRUD chains, FK dependencies, ETag/soft-delete flags.
47
47
  - `.api-fixtures.yaml` — **manifest** of required `{{vars}}` (read-only, auto-generated).
48
48
 
49
- Plus a sibling `.env.yaml` that you (or `zond prepare-fixtures`) fill with
50
- the **values** for those vars. The manifest/values split is strict — see
51
- the [workspace contract](AGENTS.md#workspace-contract) for details.
49
+ Plus a sibling `.env.yaml` that **you** fill with the **values** for those
50
+ vars (`zond prepare-fixtures` only reports which are missing). The
51
+ manifest/values split is strict — see the
52
+ [workspace contract](AGENTS.md#workspace-contract) for details.
52
53
 
53
54
  Run `zond refresh-api <name> [--spec <new-source>]` to re-snapshot when the
54
55
  upstream spec changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirrosh/zond",
3
- "version": "0.23.0",
3
+ "version": "0.26.0",
4
4
  "description": "API testing platform — define tests in YAML, run from CLI, generate from OpenAPI specs",
5
5
  "license": "MIT",
6
6
  "module": "index.ts",
@@ -34,8 +34,7 @@
34
34
  "schemas": "bun run scripts/emit-json-schemas.ts",
35
35
  "schemas:check": "bun run scripts/emit-json-schemas.ts --check",
36
36
  "lint:dead": "knip --reporter compact",
37
- "build": "bun build --compile src/cli/index.ts --outfile dist/zond && bun run scripts/codesign-darwin.ts ./dist/zond",
38
- "bench:api": "bun benchmarks/api/server.ts"
37
+ "build": "bun build --compile src/cli/index.ts --outfile dist/zond && bun run scripts/codesign-darwin.ts ./dist/zond"
39
38
  },
40
39
  "devDependencies": {
41
40
  "@types/bun": "latest",
package/src/CLAUDE.md ADDED
@@ -0,0 +1,112 @@
1
+ # src/ — architecture map
2
+
3
+ Этот файл — точка входа в **код** zond. Workspace-контракт (`.api-fixtures.yaml` vs `.env.yaml`) — в [`../AGENTS.md`](../AGENTS.md), пользовательский CLI-референс — в [`../ZOND.md`](../ZOND.md).
4
+
5
+ zond — API hygiene scanner. **Dumb-tool**: умеет дёргать API, собирать evidence, проверять конформность спеки. Не зовёт LLM, не делает решения — это работа агента, который скармливает zond'у YAML и читает обратно отчёты.
6
+
7
+ ### Litmus-тест: что кладём в zond, а что оставляем агенту
8
+
9
+ m-24 срезал эвристический слой (discovery/seed/cascade, severity-калибраторы, anti-FP-гейты, annotate-auto). Чтобы он не наползал обратно по одному «разумному» фиксу за раз — **каждое изменение zond проходит один тест: детерминировано ли оно?** Один и тот же вход → один и тот же выход, без догадок про намерение / вину / серьёзность / «баг ли это на самом деле».
10
+
11
+ - ✅ **В zond** (детерминированное, быстрое, скучное): слать запросы, валидировать схемы, считать диффы, эмитить evidence + closed-enum хинты, **корректно ограничивать scope чека** (какие case-kinds / методы он оценивает), плюс plumbing/скорость/артефакты. zond — удобный работающий быстрый инструмент.
12
+ - ❌ **Агенту** (суждение): severity, приоритет, атрибуция вины (spec vs backend), «это false-positive?», «это эксплуатируемо?», выдумывание фикстур, многопроходный discovery. Сложное решает умный агент, читая сырой evidence.
13
+
14
+ Практическое следствие для «zond что-то пропустил/зашумил» из аудита: **чини scope чека детерминированно** (напр. ARV-340 — чек смотрел не те case-kinds) — но **не добавляй suppression/down-rank «это FP»** (это ровно тот anti-FP-гейт, что убрал ARV-337; FP отсекает агент в триаже). `recommended_action` — самая размытая из выживших поверхностей: это тонкий routing-хинт, держи его тупым, не выращивай в мини-классификатор.
15
+
16
+ **Evidence-over-inference (ARV-376):** статические инференсеры по форме URL (какой `/list` владеет `{id}`, какой ресурс за `{code}`) имеют бесконечный хвост идиосинкразий спеков. **Не** закрывай хвост растущими хардкод-списками маркеров/глаголов (`LIST_VERB_SUFFIXES`, `ACCESSOR_MARKER_SEGS`, версии) — это ровно тот creep «по одному разумному за раз». Правило: **новый маркер/глагол добавляется только под реальный спек, который его упражняет**, никогда «на всякий случай» (так был убран спекулятивный `bycode`). А когда вывод неуверенный — не тупик (`miss-no-list`), а **`item.candidates`**: правдоподобные эндпоинты, ранжированные по структурной близости, БЕЗ выбора значения. Граф — детерминизм zond'а; выбор — суждение агента. Выравнивание двух слоёв, где один уже что-то делает (напр. version-strip, ARV-381), — это консистентность, не новое знание, и ок.
17
+
18
+ ## Top-level layout
19
+
20
+ | Каталог | Назначение |
21
+ |---|---|
22
+ | `cli/` | CLI surface: парсинг argv, регистрация команд, форматирование вывода. |
23
+ | `core/` | Бизнес-логика: probes, checks, generators, runner, reporters. **Никакой зависимости от commander/process.argv** — это переиспользуемое ядро. |
24
+ | `db/` | SQLite-слой: schema, migrations, queries для истории runs/results. |
25
+
26
+ **Правило**: `cli/commands/<cmd>.ts` парсит флаги и зовёт функции из `core/`. Бизнес-логика в `cli/` — code-smell (см. ARV-257 про `bootstrap.ts`/`discover.ts`, которые лежат в `cli/commands/` но не регистрируют команды).
27
+
28
+ ## cli/
29
+
30
+ | Файл | Что |
31
+ |---|---|
32
+ | `index.ts` | Entry point бинаря. |
33
+ | `program.ts` | Commander root: регистрирует все top-level команды. |
34
+ | `argv.ts`, `resolve.ts` | Аргумент-парсинг и `--api` chain resolution (per-command > global > `ZOND_API` > `.zond/current-api`). |
35
+ | `runtime.ts` | Глобальный runtime context (`api`, workspace paths, http auditor). |
36
+ | `output.ts`, `json-envelope.ts`, `json-schemas.ts` | Forматирование результатов: текстовый/JSON envelope + Ajv-валидация envelope-схем. |
37
+ | `status-filter.ts` | Общий парсер `--status` фильтров. |
38
+ | `commands/` | Один файл на команду; `commands/init/`, `commands/api/`, `commands/probe/` — кластеры с subcommands. |
39
+
40
+ Контракт команды: парсит флаги, валидирует через зависимости из `core/`, эмиттит результат через `output.ts` (envelope) или streaming-репортер.
41
+
42
+ ## core/ — подсистемы
43
+
44
+ | Подкаталог | Роль |
45
+ |---|---|
46
+ | `parser/`, `spec/` | Парсинг OpenAPI, dereferenced spec.json, extraction схем. |
47
+ | `generator/` | Синтез тестовых YAML-сьютов из spec: `suite-generator.ts`, `data-factory.ts`, `resources-builder.ts`. |
48
+ | `runner/` | HTTP-исполнение: `executor.ts`, retry, assertions, schema validation. |
49
+ | `checks/` | Schemathesis-style depth checks (status_code_conformance, response_schema_conformance, idempotency, …). Орк — `checks/runner.ts`. Один файл = один check class. |
50
+ | `probe/` | Активные security/mass-assignment probes. Сейчас два монолита (`security-probe.ts`, `mass-assignment-probe.ts`) — кандидаты на split (ARV-295, ARV-296). |
51
+ | `lint/` | Static spec-lint (`check spec`). Не путать с `checks/` — это разные команды и разные envelope'ы. |
52
+ | `diagnostics/` | Triage финдингов: классификация ошибок, hints, `recommended_action` mapping. |
53
+ | `severity/` | Per-finding severity calibration (ARV-283–288 актуализирует). |
54
+ | `anti-fp/` | Anti-false-positive guard'ы; registry-pattern частично (см. ARV-259). |
55
+ | `coverage/` | Покрытие endpoint'ов: test-runs + audit-runs, dual-metric. |
56
+ | `reporter/` | Форматирование `Run`/`Check`/`Probe` результатов в текст/JSON/NDJSON/SARIF/JUnit/HTML. |
57
+ | `exporter/` | HTML-отчёты, case studies. |
58
+ | `audit/` | Высокоуровневая `audit` команда — wraps checks + probe + report для CI-smoke. |
59
+ | `workspace/` | Layout API-папки: `apis/<name>/{spec,catalog,resources,fixtures,env,secrets}`. |
60
+ | `context/`, `identity/`, `secrets/` | Загрузка контекста запуска, identity tokens, секреты. |
61
+ | `util/`, `utils.ts` | Общие хелперы. URL/headers/schema-валидация частично дублируются с `probe/shared.ts` — кандидат на консолидацию (ARV-297). |
62
+ | `selectors/`, `meta/`, `classifier/` | Endpoint-classification и meta-аттрибуты для дискавери и группировки. |
63
+ | `setup-api.ts` | Регистрация нового API в workspace (used by `add api`/`refresh-api`). |
64
+
65
+ ## db/
66
+
67
+ SQLite на bun. `migrations/` — versioned migrations, `schema.ts` — текущий schema, `queries/` — типизированные SQL. Используется reporter'ами и `coverage` для исторических runs. Retention (ARV-266): `zond db stats` — счётчики строк per `run_kind`; `zond db prune` — opt-in удаление (per-kind defaults: check/probe/request/fixture старше 7d, `regular` — forever; `--older-than 30d` для uniform-cutoff), VACUUM после delete.
68
+
69
+ ## Data-flow по фазам
70
+
71
+ zond работает по 5 фазам — это ментальная модель CLI и skills:
72
+
73
+ ```
74
+ Setup ──► Generate ──► Run ──► Analyze ──► Report
75
+ ```
76
+
77
+ | Фаза | Команды | Что происходит | Артефакты |
78
+ |---|---|---|---|
79
+ | **Setup** | `init`, `add api`, `refresh-api`, `use`, `doctor`, `prepare-fixtures` | Регистрируем API, тянем spec, заполняем `.env.yaml`. | `apis/<name>/{spec.json, .api-catalog.yaml, .api-resources.yaml, .api-fixtures.yaml, .env.yaml}` |
80
+ | **Generate** | `generate`, `api annotate`, `prepare-fixtures` | Синтез тестовых YAML из spec + аннотации (seed-bodies, idempotency, pagination, lifecycle). | `apis/<name>/tests/*.yaml`, annotations |
81
+ | **Run** | `run`, `session`, `request`, `audit`, `checks run`, `probe <class>` | Дергаем API: тесты, depth-checks, probes. Пишем runs/results в SQLite. | `runs/` (DB), HTTP-аудит |
82
+ | **Analyze** | `coverage`, `db {runs,run,collections,diagnose,compare}`, `check spec`, `describe`, `catalog` | Триаж результатов, coverage-гэпы, lint спеки. Финдинги через `recommended_action`. | Envelope JSON, finding-streams |
83
+ | **Report** | `report`, `report-bundle`, `--report {json,ndjson,sarif,junit,html,markdown}` | Конвертация runs/findings в shareable форматы. | NDJSON-stream, SARIF, HTML, JUnit |
84
+
85
+ Полная картина с iron-rules и pre-flight checklist'ами — в skill-документах (`src/cli/commands/init/templates/skills/*.md`), которые ставятся через `zond init`.
86
+
87
+ ## Extension points
88
+
89
+ Когда добавляешь новую функциональность — это **типичные точки расширения**:
90
+
91
+ | Хочу добавить | Куда |
92
+ |---|---|
93
+ | Новый depth-check (schemathesis-style) | `core/checks/checks/<name>.ts` + регистрация в `core/checks/runner.ts`. |
94
+ | Новый probe-class (security/mass-assignment-like) | `core/probe/<class>/` подмодуль; subcommand в `cli/commands/probe/`. Не клади всё в один монолит — см. ARV-295/ARV-296. |
95
+ | Новый reporter format | `core/reporter/<format>.ts`; зарегистрировать в `cli/output.ts` или per-command `--report`. |
96
+ | Новый CLI флаг с envelope-выводом | `cli/commands/<cmd>.ts` + Ajv-схема в `cli/json-schemas.ts`. Envelope shape — единый, не плоди вариации. |
97
+ | Анти-FP guard для check/probe | `core/anti-fp/rules/<rule>.ts` (registry-pattern в развитии — ARV-259). |
98
+ | Новая diagnostic-hint | `core/diagnostics/` — closed-enum `recommended_action`, не магические строки. |
99
+ | Новая DB-сущность | `src/db/migrations/<NNNN>-<name>.sql` + tiped query в `db/queries/`. |
100
+
101
+ ## Workspace contract (ссылка)
102
+
103
+ Артефакты в `apis/<name>/` (`spec.json` / `.api-catalog.yaml` / `.api-resources.yaml` / **`.api-fixtures.yaml` manifest** / **`.env.yaml` values**) — описаны в [`../AGENTS.md`](../AGENTS.md). Главное правило: **manifest — source of truth о списке переменных, env — только values**.
104
+
105
+ ## Conventions
106
+
107
+ - TypeScript strict mode, `bun run check` = `tsc --noEmit` без warnings (`noUnusedLocals`/`noUnusedParameters` включены).
108
+ - Тесты — `bun test` (unit + integration), `bun run test:mocked` (HTTP-mocked).
109
+ - Сборка — `bun run build` → single-file `dist/zond` бинарь.
110
+ - Никаких `console.log` в `core/` — выводи через `cli/output.ts` или reporter slot.
111
+ - Никаких SDK Anthropic/Ollama/прочих LLM-провайдеров внутри zond — это dumb-tool (см. `feedback_zond_no_llm_calls`).
112
+ - Финдинги — через closed-enum `recommended_action`, агент триажит по enum, не по тексту message.
@@ -19,11 +19,13 @@ import { printError, printSuccess } from "../output.ts";
19
19
 
20
20
  export interface AddApiOptions {
21
21
  name: string;
22
- spec?: string;
22
+ /** One or more specs. Multiple → deterministic union (ARV-375). */
23
+ specs?: string[];
23
24
  baseUrl?: string;
24
25
  dir?: string;
25
26
  force?: boolean;
26
27
  insecure?: boolean;
28
+ caPath?: string;
27
29
  dbPath?: string;
28
30
  json?: boolean;
29
31
  }
@@ -31,7 +33,7 @@ export interface AddApiOptions {
31
33
  export async function addApiCommand(opts: AddApiOptions): Promise<number> {
32
34
  const ws = findWorkspaceRoot();
33
35
  if (ws.fromFallback) {
34
- const m = `No workspace detected (no zond.config.yml / .zond / zond.db / apis marker). Run \`zond init\` first to bootstrap a workspace.`;
36
+ const m = `No workspace detected (no zond.config.yml / .zond / apis marker). Run \`zond init\` first to bootstrap a workspace.`;
35
37
  if (opts.json) printJson(jsonError("add-api", [m])); else printError(m);
36
38
  return 2;
37
39
  }
@@ -39,16 +41,18 @@ export async function addApiCommand(opts: AddApiOptions): Promise<number> {
39
41
  const envVars: Record<string, string> = {};
40
42
  if (opts.baseUrl) envVars.base_url = opts.baseUrl;
41
43
 
44
+ const hasSpec = (opts.specs?.length ?? 0) > 0;
42
45
  let result: SetupApiResult;
43
46
  try {
44
47
  result = await setupApi({
45
48
  name: opts.name,
46
- spec: opts.spec,
49
+ specs: opts.specs,
47
50
  dir: opts.dir,
48
51
  envVars: Object.keys(envVars).length > 0 ? envVars : undefined,
49
52
  dbPath: opts.dbPath,
50
53
  force: opts.force,
51
54
  insecure: opts.insecure,
55
+ caPath: opts.caPath,
52
56
  });
53
57
  } catch (err) {
54
58
  const m = (err as Error).message;
@@ -63,7 +67,7 @@ export async function addApiCommand(opts: AddApiOptions): Promise<number> {
63
67
  return 2;
64
68
  }
65
69
 
66
- const mode: "spec" | "run-only" = opts.spec ? "spec" : "run-only";
70
+ const mode: "spec" | "run-only" = hasSpec ? "spec" : "run-only";
67
71
  const artifacts = mode === "spec"
68
72
  ? ["spec.json", ".api-catalog.yaml", ".api-resources.yaml", ".api-fixtures.yaml", ".env.yaml"]
69
73
  : [".env.yaml"];
@@ -106,15 +110,22 @@ export function registerAdd(program: Command): void {
106
110
  add
107
111
  .command("api <name>")
108
112
  .description("Register an API: from an OpenAPI spec (full toolkit) or just --base-url (run-only mode)")
109
- .option("--spec <path>", "Path or URL to OpenAPI spec — enables generate/probe/validate-schema")
113
+ .option(
114
+ "--spec <path>",
115
+ "Path or URL to OpenAPI spec — enables generate/probe/validate-schema. Repeat --spec to union multiple specs (e.g. v1 + v2) into one merged audit target (ARV-375).",
116
+ (val: string, acc: string[]) => acc.concat(val),
117
+ [] as string[],
118
+ )
110
119
  .option("--base-url <url>", "Base URL recorded in .env.yaml (required if --spec is omitted)")
111
120
  .option("--dir <path>", "Target directory (defaults to apis/<name>/)")
112
121
  .option("--force", "Overwrite an existing API with the same name")
113
122
  .option("--insecure", "Skip TLS verification when fetching the spec from https")
123
+ .option("--ca <path>", "PEM CA bundle to trust for the spec fetch (adds to public roots; also reads NODE_EXTRA_CA_CERTS) — use instead of --insecure for internal/corp CAs")
114
124
  .option("--db <path>", "Path to SQLite database file")
115
125
  .action(async (name: string, opts, cmd: Command) => {
116
126
  const json = globalJson(cmd);
117
- if (!opts.spec && !opts.baseUrl) {
127
+ const specs: string[] = Array.isArray(opts.spec) ? opts.spec : (opts.spec ? [opts.spec] : []);
128
+ if (specs.length === 0 && !opts.baseUrl) {
118
129
  const m = "Provide --spec <path|url> for a full registration, or --base-url <url> for run-only mode.";
119
130
  if (json) printJson(jsonError("add-api", [m])); else printError(m);
120
131
  process.exitCode = 2;
@@ -122,11 +133,12 @@ export function registerAdd(program: Command): void {
122
133
  }
123
134
  process.exitCode = await addApiCommand({
124
135
  name,
125
- spec: opts.spec,
136
+ specs,
126
137
  baseUrl: opts.baseUrl,
127
138
  dir: opts.dir,
128
139
  force: opts.force === true,
129
140
  insecure: opts.insecure === true,
141
+ caPath: typeof opts.ca === "string" ? opts.ca : undefined,
130
142
  dbPath: typeof opts.db === "string" ? opts.db : undefined,
131
143
  json,
132
144
  });