7 Commits
Author SHA1 Message Date
glm-5.3-flash c89aa65fb2 chore(deps): consume alkcall 0.9.0
- bump alkcall 0.8.1 -> 0.9 (gateway feature unchanged). No code change
  forced: none of the 0.9.0 breaking surfaces are constructed or
  pattern-matched in this crate — no ManagerError match arms (the new
  InvalidAdopt variant changes nothing here), no RelayError matching
  (the removed PubTypedOpen variant is unreachable from this crate),
  no Establishment construction or Debug dependence (the WS ferry
  passes Option<OpenEstablisher> through unchanged, per the 0.8.0
  audit), and the op/register forward-through refactor does not touch
  the from_wss import filter or the adapter-registered leaf bundles
- ride-through fixes: tests/ws_overlay_ops.rs now expects
  register_imported's new Result<(), String> return (8 sites assert
  registration success instead of discarding the value — the bool
  return they consumed was ignored anyway); fuzz/shared follows the
  main crate to alkcall 0.9 so the harness replay resolves one
  alkcall version (the stale 0.8.1 spec pinned two copies against
  alkhttp's requirement and broke the shared-crate build)
- minor bump 0.6.0 -> 0.7.0 per the dep-wave convention (0.3.0 rode
  alkcall 0.6.0, 0.4.0 rode 0.7.0, 0.5.0 rode 0.8.0): this crate's
  public API carries alkcall types through re-exports and the
  ProtocolHandler impl, so a semver-breaking upstream is
  semver-relevant here

Verification: cargo test 441+70 pass (default), 520+70 pass
(all-features), clippy --all-features --all-targets -D warnings clean,
fmt --check clean, doc --no-deps clean, fuzz corpus replay 21/21
2026-10-14 05:19:29 +00:00
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