feat(fuzz): target 3 spec_import — import-surface fuzz harness + 42 seeds
- fuzz/shared/src/spec_import.rs: invariants over the import-parse
surface — the three OpenAPISpec constructors (str/json/yaml,
ADR-051 JSON-first), the YAML→JSON normalization boundary
(reject-loudly partition: non-finite floats, pointer keys), the
compile/harden/validate lane (hardening partition, validate
determinism, closed-by-default unknown-key probe), the FromMCP
build_spec tool-shape lane, and the $ref-bounded resolver lane
(cycles/hop chains/deep nesting fail cleanly, no hang, no stack
blowout).
- fuzz/fuzz_targets/spec_import.rs: thin fuzz_target! wrapper.
- 42 committed corpus seeds via gen_fuzz_seeds.py (valid JSON/YAML
docs, structural failures, YAML normalization edges, $ref cycles/
chains/unresolvable/external, schema-validation edges, MCP
tool-shape edges); corpus-replay gate count floor 12.
- fuzzing exposure hubs next to the wave-1 one:
openapi_spec::fuzzing, input_validation::fuzzing (opaque
FuzzCompiledValidator newtype — the pub(crate) validator never
leaks), from_mcp::fuzzing (mcp feature).
- fuzz/shared gains openapi/mcp features (default on); json.dict
extended with the OpenAPI token set (75 unique tokens).
- clippy follow-up in wave-1's ws_chunk_framing replay test
(unnecessary u32 casts against the newer lint).
- docs/plans/fuzzing.md §3/§6 target 3 → implemented; fuzz/README
target table + campaign-advice lines updated.
Verification: corpus replay 11/11 green (default and
--no-default-features), cargo fuzz build clean, main crate 457
default / 590 all-features tests green, clippy+fmt clean both crates.
Campaign: detached 10-min smoke run — see docs/plans/fuzzing.md §5.