fuzz-target-payload-codec: the §3.1 target — all five invariants in shared, thin fuzz_target wrapper, 97 committed seeds + gitignore negation + README row; placeholder bin replaced; serde (derive) added to shared

This commit is contained in:
glm-5.3-flash committed 2026-10-10 21:42:41 +00:00
1 parent 21ab6fe564
commit 1f6cc0970a
108 files changed
+583 -24

No files matched your search

+71 -9
View File
@@ -1,7 +1,7 @@
---
id: fuzz-target-payload-codec
name: Fuzz target — `payload_codec` (core payload encode/decode invariants)
status: pending
status: completed
depends_on: [fuzz-scaffold]
scope: moderate
risk: low
@@ -54,19 +54,19 @@ replay.
## Acceptance Criteria
- [ ] `fuzz_targets/payload_codec.rs` + `shared/src/payload_codec.rs`
- [x] `fuzz_targets/payload_codec.rs` + `shared/src/payload_codec.rs`
exist; `cargo fuzz build` builds the target (nightly, ASAN)
- [ ] All five §3.1 invariants implemented in `shared` and exercised by
- [x] All five §3.1 invariants implemented in `shared` and exercised by
both the fuzz path and the `#[cfg(test)]` replay module
- [ ] Seed corpus committed (`fuzz/corpus/payload_codec/`), generated
- [x] Seed corpus committed (`fuzz/corpus/payload_codec/`), generated
by `python3 fuzz/gen_fuzz_seeds.py` (deterministic — re-running
is a no-op diff); gitignore negation in place
- [ ] `cargo test --manifest-path fuzz/shared/Cargo.toml` green
- [x] `cargo test --manifest-path fuzz/shared/Cargo.toml` green
(replays the committed corpus through the invariants)
- [ ] `shared` clippy/fmt gates green
- [x] `shared` clippy/fmt gates green
(`cargo clippy --manifest-path fuzz/shared/Cargo.toml
--all-targets -- -D warnings`, `fmt --check`)
- [ ] Root workspace gates still green
- [x] Root workspace gates still green
## References
@@ -81,8 +81,70 @@ replay.
## Notes
> To be filled by implementation agent
- **serde added to `shared`'s manifest** with `derive` (beyond the
spec's manifest list of `alkstore`/`serde_json`/`arbitrary`): the
typed round-trip needs a representative *struct* payload
(`TestJobPayload` — string/int/vec/option/map fields, the consumer
payload shape), and `#[derive(Serialize, Deserialize)]` requires the
direct dep for the bounds. `serde_json` re-exports nothing derive-able;
core already depends on serde transitively.
- **The typed-failure partition is exercised four ways**: over `Value`,
`TestJobPayload`, `String`, and `Vec<Value>` decodes of the raw
input — each failure must be `Error::Codec` (the ADR-023 §1 pin),
each success is then driven back through `encode_payload` →
`payload_as::<Value>` for input-closure Value round-trip
(canonicalization is serde_json's: duplicate-key last-write-wins
etc., pinned-not-regressed, not re-derived).
- **Deep-input loudness probes**: raw nested-array strings at 4x and
past-4096 depth decode → `Err(Codec)` (both `Value` and typed);
a constructed `Value` nested one past the 128 limit encodes fine
(the serializer's recursion is unbounded but shallow here) and its
stored bytes decode → `Err(Codec)` — the stored-deep-form loudness
the ADR-023 §1 posture wants. Encode is deliberately NOT probed at
extreme depth (serializer recursion would overflow at absurd
depths); 129 + shallow-8 form the pair. serde_json's 128 limit is
pinned as `RECURSION_LIMIT`; a future serde_json bump failing these
probes is the intended loud signal, not a fragility.
- **`arbitrary` path** runs `Unstructured::new(data)` → up to two
`JsonValue`s from the same bytes (exhaustion → skip, the raw path
already covers it): encode Ok, decode == v, plus a typed-stability
check for arbitrary values that happen to decode as `TestJobPayload`.
- **Placeholder target removed** per the scaffold's lifecycle note —
`payload_codec`'s `[[bin]]` replaces it; the two remaining wave-1
target tasks still add their own bins.
- **Seeds came out at 97** (spec list expanded: per-prefix truncations
of two docs, depth probes at 8/127/128/129/256/1024 for arrays and
8/200 for objects, plus the loud-fail shapes NaN/Infinity/+1/01/.5/
1./--1/lone-key/missing-value). Replay-floor set at ≥ 64.
- One behavior surfaced by the corpus: `payload_as::<Value>` of
deep/sub-limit vs shallow shapes needed the round-trip battery's
constructed `Value` to match the probe doc's leaf (`0`, not `Null`)
— fixed during implementation; the final replay is green.
## Summary
> To be filled on completion
Landed the §3.1 `payload_codec` fuzz target: `fuzz_targets/payload_codec.rs`
(the thin `fuzz_target!` wrapper, replacing the scaffold placeholder's
`[[bin]]`) and `fuzz/shared/src/payload_codec.rs` implementing all five
§3.1 invariants — no-panic baseline via the four-way typed decode
partition (`Value` / `TestJobPayload` / `String` / `Vec<Value>`),
deep-input loudness at serde_json's recursion limit (raw-byte probes
and the stored-deep-form probe), empty-byte-store loudness (the ADR-023
§1 corruption note), the `Value` round-trip (fixed battery + raw-input
closure + arbitrary `JsonValue`s from the bounded-depth helper), and
the typed round-trip over a representative payload set (derived struct
+ std types). 97 deterministic seeds committed under
`fuzz/corpus/payload_codec/` (generated by `gen_fuzz_seeds.py`'s new
`payload_codec_seeds()`, re-run is a no-op), with the `.gitignore`
negation and the README targets-table row. `fuzz/shared/Cargo.toml`
gained `serde` (derive) — no other manifest changes; `fuzz/Cargo.lock`
updated.
Verified: `cargo test --manifest-path fuzz/shared/Cargo.toml` green
(corpus replay 97/97 + invariant unit probes); shared clippy
`-D warnings` + `fmt --check` green; `cargo fuzz build` green from
`fuzz/` (nightly/ASAN binary at
`target/x86_64-unknown-linux-gnu/release/payload_codec`); root
workspace `cargo build` / `cargo test` (all suites pass) / clippy
`-D warnings` / `fmt --check` green. Campaign flags remain the
campaigns-and-gate task's concern.