6 Commits
Author SHA1 Message Date
glm-5.3-flash 4b32f84bd2 chore: release alkhttp 0.6.0
The fuzzing adoption waves (commits 5971c0a..a16dee7) plus the alkcall
0.8.1 patch pickup, released as a minor bump:

- Added: fuzz/ infra (5 nightly targets, stable-toolchain corpus-replay
  gate), public SSE parse types (SseParser/SseEvent/SseParseError/
  SSE_EVENT_BUFFER_CAP), the WriteChunkWalk/Walk chunk-walk API, and
  the fuzzing re-export hubs.
- Fixed: FWD-20 (SSE dribble drain quadratic -> linear), FWD-21
  (WriteChunkWalk failure arms carry the pre-failure pieces), FWD-22
  (harness re-frame guard-CR; no crate change).
- Changed: alkcall 0.8.1 (duplicate adopt/open collision fix).
- Doc: fixed broken intra-doc link surfaced by SSE_EVENT_BUFFER_CAP
  going public.

Verified: cargo test (457 passed), cargo test --all-features (590),
cargo clippy --all-targets -- -D warnings, cargo fmt --check,
cargo doc --no-deps (0 warnings), fuzz corpus replay (21 passed),
cargo publish --dry-run --allow-dirty.
2026-09-29 21:02:32 +00:00
glm-5.3-flash b78b9a2618 fix(fuzzing): FWD-22 — guard-CR re-frame makes the SSE round-trip total
The sse_parser re-frame probe emitted 'data: <line>' unguarded, so a
payload CR (interior CR in multi-line data: the payload line is '\r')
was consumed by strip_cr at re-parse time and the round-trip lost it.
Wave 1 had skipped only the terminal-CR case. Found ~32 min into the
first 45-min sse_parser campaign.

- frame_event emits 'data: <line>\r': the guard CR is the line
  terminator, strip_cr removes exactly it, the payload CR survives
- drop the terminal-CR skip; the round trip is now total over
  JSON-encodable payloads (JSON never encodes a raw CR, so the
  residual asymmetry is the serde_json non-preserved key-order class)
- no crate-side change: the parser was correct (data: x\r\r\n ->
  x\r); only the harness re-encode was weak
- regression: 2 new seeds (interior CR via \r escape; raw CR via CRLF
  framing, two-line doc) + unit cases

Verification: shared 21/21 (default + no-default-features), main
crate green, clippy -D warnings clean, fmt clean, cargo fuzz build
clean
2026-09-29 19:25:39 +00:00
glm-5.3-flash e023d3275e feat(fuzz): target 4 stateful wave — ws_session_opseq + retry_throttle
Two stateful op-sequence harnesses (fuzz/shared):

- ws_session_opseq: the WS byte adapter's generic pumps driven
  end-to-end over a mock socket flavor through the new
  byte_adapter::fuzzing drive hub (FuzzMockSocket flavor,
  FuzzChannels/FuzzWriteSide channel bundle, pump spawn funnels,
  FuzzInboundChunkProgress). Asserts lossless inbound forwarding
  (exact after the teardown joins), the WS-01 walk's completed-chunk
  count vs the declared frame count, the WS-15 close mapping
  (1002/1011/verbatim-requested + per-cause reasons), lossless write
  framing onto the declared chunk stream (FWD-21's failure-carry
  included), lane-failure isolation (the alkcall 0.8.1 class), and
  pump termination within the bound.
- retry_throttle: the inlined RetryAfterMiddleware's throttle map
  through the new client::fuzzing hub — capacity bound, the
  expired-first + Farthest-future eviction policy, the ceiling clamp,
  refresh contraction, the Retry-After parse partition, and the
  jitter bounds.

16 committed seeds (8 per target) in gen_fuzz_seeds.py, hand-encoded
against the arbitrary 1.4.x derive layout with ceiling-division
variant tags and per-element keep-going bytes, pinned by
seed-decode tests. fuzz/shared gains bytes/futures/tokio/url/
http/httpdate/arbitrary deps.

Verification: main crate 441 default tests, fuzz shared replay 21/21
(default + no-default-features), cargo fuzz build clean (5 targets),
clippy+fmt clean both crates.
2026-09-29 17:28:20 +00:00
glm-5.3-flash 0ae53d2d27 fix(websocket): WriteChunkWalk failure arms carry the parsed pieces
FWD-21, found minutes into the first ws_session_opseq campaign: one
feed that drains a complete chunk and then hits an oversize header
(or the pending cap) in the same call returned
Walk::Oversize/PendingOverCap and silently discarded the already
drained pieces — the walk's own consumption-accounting contract
('every parsed byte is either emitted or retained', wave-1
invariant 6) was violated by the crate. Production could not reach
the shape (the mux's per-write invariant caps pending at one chunk +
header), so impact was nil, but WriteChunkWalk is public API and the
wave-1 split_walk equality check was skipped on exactly this path.

- Walk::Oversize(Vec<Vec<u8>>) / Walk::PendingOverCap(Vec<Vec<u8>>)
  carry the pre-failure pieces (user-approved semver-relevant API
  change)
- run_write_pump emits the carried pieces before fail_write_pump, so
  a 1011 close never swallows a legal chunk the mux already counted
  as written
- the ws_chunk_framing harness's split-drive failure arm now
  reconciles the parsed-before-failure bytes across the whole and
  split drives; a regression seed covers the drain-then-fail shape

Verification: main crate 441 default / 520 all-features tests, fuzz
shared replay 21/21 (default + no-default-features), cargo fuzz build
clean, clippy+fmt clean both crates.
2026-09-29 17:28:14 +00:00
glm-5.3-flash e6491e31c7 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.
2026-09-29 10:06:03 +00:00
glm-5.3-flash 5971c0a0d6 fuzz: adopt fuzzing (wave 1) + fix the SSE dribble quadratic (FWD-20)
Adopt the alk*/fuzzing posture (alkcall 0.8.1 precedent) — the dep
bump this ride-along builds on was 4005517:

- docs/plans/fuzzing.md: the four-target plan (sse_parser,
  ws_chunk_framing this session; spec_import + the stateful wave
  later), the infrastructure blueprint, and the corpus/campaign
  policies (§3's table tracks progress)
- fuzz/ workspace (the sibling layout): nightly-pinned subtree,
  stable fuzz/shared invariant library (corpus replay = the standing
  fuzz gate), detached runner (never a foreground child of an agent
  session), JSON dict, deterministic seed generator; 80 committed seeds
- two wave-1 targets: sse_parser (SseParser::feed + the FWD-17
  envelope mapping — the parser every subscription forwards through)
  and ws_chunk_framing (WriteChunkWalk, the write pump's chunk walk)
- crate-side surfacing: SseParser/SseEvent/sse_event_envelope are pub
  behind the adapters::forward::fuzzing re-export hub; the write
  pump's inline chunk loop extracted as WriteChunkWalk (pump byte
  behavior unchanged — 441 tests green); retained_bytes() diagnostic
  (the alktunnels/alksocks posture)
- root Cargo.toml gains the explicit [workspace] table with
  fuzz/ excluded (auto-discovery would pull the nightly-consumed
  dev-deps into the stable build — the alkcall trap); fuzz/ joins the
  publish exclude list
- AGENTS.md verification checklist gains the corpus-replay line

FWD-20 fix, found by driving the new sse_parser harness: feed()
rescanned the whole reassembly buffer per feed — a dribble-fed
subscription (one unterminated line over many small chunks) cost
quadratic CPU (1 MiB dribble ≈ minutes per connection, unbounded
total). Line assembly now walks only the arriving chunk: the buffer
holds just the current line, the scan never revisits drained bytes.
1 MiB dribble: ~5 min (quadratic) → 21 ms (linear). Regression test
sse_parser_dribble_feeds_do_not_rescan_the_buffer.

Verification: cargo test 441 passed; cargo test --all-features ok;
clippy --all-targets -- -D warnings (main + fuzz/shared) clean;
fmt --check (main, fuzz, fuzz/shared) clean; cargo fuzz build clean;
corpus replay (cargo test --manifest-path fuzz/shared/Cargo.toml) 80
seeds green.

Refs: alkcall docs/research/fuzzing.md (§7.9 posture), alksocks
08a3694 (the sibling wave-1 shape), docs/plans/fuzzing.md §2
2026-09-29 08:32:29 +00:00