- version 0.8.1 -> 0.8.2 (patch: behavior-only bug fix in the local
feature's PTY waiter from db0b1fc; ADR-004 decision 1's negative-code
convention now holds on the PTY path; no API, wire-format, or
dependency change)
- CHANGELOG entry for 0.8.2 (review #004 resolution: the waitpid reap
mechanism, the signal round-trip notes, the tests, the docs)
- finish the em-dash sweep in prose 58da42a missed: doc comments in
tests/ (negotiation, pipe, pty, common) and fuzz/shared
(control_json, negotiation_frame, session_opseq); prose rewrites
only, no code, JSON literals, or wire shapes touched
- README checked: no version pins, nothing to update
Verification: cargo test; cargo test --all-features; cargo clippy
--all-targets -D warnings (default and --all-features); cargo fmt
--check; cargo doc --no-deps; fuzz/shared corpus replay (9 seeds pass);
cargo check + clippy for wasm32-unknown-unknown -D warnings; cargo
publish --dry-run --allow-dirty (43 files, 194.4 KiB); detached
campaigns over the four targets (chunk_frame 632s, negotiation_frame
602s, control_json 610s, session_opseq 608s) all exiting 0 with
0 crash/oom/timeout findings.
alktty fuzzing
cargo-fuzz targets for the wire-facing parse surfaces. The design and
operating rules live in docs/plans/fuzzing.md (adopted from alkcall's
docs/research/fuzzing.md); this README is the operational cheat-sheet.
Layout
fuzz_targets/: nightly-onlyfuzz_target!binaries (thin wrappers).shared/is the stable-toolchain library holding the invariant logic; the corpus replay tests run here on plaincargo test.corpus/<target>/holds the committed seeds (regenerate withpython3 fuzz/gen_fuzz_seeds.py).artifacts/: gitignored crash/oom/timeout artifacts + campaign logs.
Targets
| Target | Drives |
|---|---|
chunk_frame |
ChunkReader/ChunkWriter (5-byte chunk codec, ADR-001) |
negotiation_frame |
NegotiationReader/NegotiationWriter + NegotiateRequest + error_response_bytes + the cross-codec peek-disambiguation seam |
control_json |
ControlMessage::from_slice/to_json + signal_from_name |
session_opseq |
the producer session pump (drive_session_pre_negotiated) + a fuzz-local mock backend: the stateful op-sequence target (ADR-004/ADR-005 invariants; the alkcall-bug-class hunt) |
Running a campaign (always detached)
Agent sessions must never run fuzzing in the foreground (an OOM in a target can take down the session host; see docs/plans/fuzzing.md §3). Use the detached runner:
fuzz/run-detached.sh chunk_frame
# poll:
tail -n 50 fuzz/artifacts/chunk_frame-*.log
ls fuzz/artifacts/chunk_frame/
pgrep -f "cargo fuzz run chunk_frame"
FUZZ_RUNTIME_SECS=1800 fuzz/run-detached.sh negotiation_frame for a
longer campaign. The runner pins -fork=1 -rss_limit_mb=2048 -malloc_limit_mb=2048 -timeout=25 and detaches via setsid + nohup.
Corpus replay (the standing fuzz gate)
cargo test --manifest-path fuzz/shared/Cargo.toml
Replays every committed seed through the same invariant functions the fuzzer runs, on stable, no nightly needed. Part of the release verification checklist (AGENTS.md).
Toolchain notes
rust-toolchain.toml pins nightly for this subtree only (llvm-tools
required by cargo-fuzz); the crate itself stays on stable at MSRV 1.88.
cargo fuzz build must be run with the fuzz dir as CWD or trust the
toolchain file (rustup resolves per directory).
Run campaigns before releases and after touching src/wire.rs,
src/negotiation.rs, src/control.rs, the adapter, or the session pump.
session_opseq notes
The op-sequence target is inherently slower than the byte-format targets
(each input drives a full tokio session with bounded reads and yields,
which means tens of exec/s per worker, not millions). That is expected: the value is
state-space exploration, not throughput. It uses tokio::time::timeout
internally, so the nightly target needs the --features time tokio
surface already present via the shared crate's deps; no extra flags.
Its invariants (kill-on-Drop, exit-chunk-is-last, sentinel/FIFO
semantics) are the ones unit tests assert individually; the fuzzer
explores interleavings no example test can enumerate.