100 Commits
Author SHA1 Message Date
glm-5.3-flash 0f16c4f2f9 chore: release alkhttp 0.8.1
- version bump 0.8.0 -> 0.8.1 (patch per the dep-wave convention:
  0.4.1 rode alkcall 0.7.1; alkcall 0.10.1 is a patch release with no
  API or wire-format change, see the preceding consume commit)
- changelog: cut [0.8.1] with the alkcall 0.10.1 consumption (the
  adopt-window ordering fix; no surface reached through the HTTP API)
- README dependency snippet 0.7.0 -> 0.8.1 (was left stale by the
  0.8.0 release)

Verification: cargo test (467), clippy --all-targets -D warnings,
fmt --check, cargo publish --dry-run clean
2026-10-22 06:29:32 +00:00
glm-5.3-flash cc80188e3d chore(deps): consume alkcall 0.10.1
Picks up the adoption-window ordering fix (alkcall review 013, routed
from alkgit's wave-2 gate): a chunk routed by the channels demux inside
the adopt_channel install->drain window was delivered ahead of
earlier-parked early arrivals, inverting the byte-stream order (alkgit
pkt-line desync), plus a related loss variant that parked a chunk
forever. This crate reaches the fixed paths via the shared
ChannelManager; no alkcall API surface used by this crate changed.

The requirement is tightened 0.10 -> 0.10.1 so downstream projects
composing this crate with alkcall resolve the fixed version rather than
an older 0.10.x lockfile entry.

Verified: cargo test (467), cargo test --all-features (530), clippy
--all-targets -- -D warnings, fmt --check, fuzz corpus replay 21/21.
2026-10-22 06:28:51 +00:00
glm-5.3-flash dfa0216186 review: post-adoption pass over the alkcall-0.10 wiring pair (0766f4b, ad63507)
Three inline findings from the session review of ADR-072/ADR-073:

- fix(auth): Basic door parsing no longer requires UTF-8 — the decoded
  password bytes pass through to resolve_from_token byte-faithfully, so
  RFC 7617 legacy ISO-8859-1 credentials resolve instead of parsing to
  None. ADR-073 already specced raw-bytes passthrough ("decoded and fed
  as the AuthToken raw bytes"); the String::from_utf8 round-trip
  contradicted it.
- test: pin the with_ws_max_sessions fix against the shape it fixed —
  a builder call after with_ws_max_sessions (the pre-router_state()
  builder silently reverted the cap).
- test: pin that ownership wiring survives a later HttpAdapter builder
  rebuild at the HTTP surface.

Verified: cargo test (451), --all-features (530), clippy --all-targets
-D warnings, fmt --check, fuzz corpus replay 21/21, cargo doc.
2026-10-17 16:25:07 +00:00
glm-5.3-flash 0766f4bebe feat(server): door-surface Basic auth tolerance (review 008)
review 008 resolution:

- AH-1: door_auth_middleware on extras doors resolves
  Authorization: Basic base64(user:pass) — password field as the
  token (GitHub-PAT shape, git-lfs / git smart-HTTP clients) — through
  the same IdentityProvider::resolve_from_token backend as Bearer; no
  new trait method, resolve-fails -> None fallback preserved. Bearer
  surfaces stay Bearer-only: the gateway endpoints, /openapi.json,
  /healthz, the MCP nest, and the WS upgrade path never resolve Basic.
  The 'Bearer-only is the auth mechanism' doc line flips (ADR-073).
- AH-2 ride-along: extras door route tables are static — the merged
  router owns the decoy fallback, a second fallback panics at
  construction; documented on with_extra_routes and ADR-046 §1.

docs: ADR-073, ADR-046 §1/§4 amendment, ADR-004 presentation table,
review 008 marked resolved; new server::* re-exports; dep:base64
behind the server feature.

Verification: cargo test (449 pass, incl. door Basic resolve-through
and gateway-stays-Bearer-only pins), test --all-features (528),
clippy --all-targets -D warnings, fmt --check, fuzz corpus replay
(21/21), cargo doc.
2026-10-17 16:09:36 +00:00
glm-5.3-flash ad63507124 feat(server): thread alkcall 0.10 ownership wiring onto the HTTP surface
Ride the alkcall 0.10 gateway ownership seam (review 012, ADR-048
amendment) into the door layer:

- HttpAdapter::with_ownership_provider /
  HttpAdapter::with_static_ownership_fallback mirror alkcall's builder
  shape; OwnershipProvider re-exported next to GatewayDispatch
- RouterState carries the wiring; GatewayState::dispatch is the single
  spine builder for both the gateway routes and the MCP nest
- loud default passes through: a resource_type-gated op on an unwired
  spine is refused (FORBIDDEN envelope -> 403/401) on HTTP exactly as
  on the native wire
- WS native-session path untouched (consults ownership natively)

fix: with_ws_max_sessions never stored the fresh session-slot
semaphore; later builder calls silently reverted the cap. Builder
state now assembled through one router_state() helper.

infra: fuzz/shared follows to alkcall 0.10 (two alkcall copies in one
build would type-clash); alkhttp 0.8.0 per the dep-wave convention.

Verification: cargo test (444 pass, incl. 3 new call_resource_gated_*
HTTP-surface pins), clippy --all-targets -D warnings, fmt --check,
test --all-features (520), fuzz corpus replay (21/21), doc.
2026-10-17 16:00:12 +00:00
glm-5.3-flash 1cac63fdba docs(review): 008 — HTTP-door consumer evidence (LFS Basic auth, route-fallback constraint)
Report-only from alkgit's LFS POC: AH-1 the shared bearer middleware
is Bearer-only while real git-lfs authenticates Basic on the batch;
AH-2 extras doors cannot carry a second route fallback (docs-only
record). No mechanism change fixed here; dispositions session-owned.
2026-10-17 07:28:45 +00:00
glm-5.3-flash 449661eb87 chore: release alkhttp 0.7.0
- version bump 0.6.0 -> 0.7.0 (minor per the dep-wave convention:
  0.5.0 rode alkcall 0.8.0; see the preceding consume commit)
- README usage snippet 0.5.0 -> 0.7.0
- changelog: cut [0.7.0] with the alkcall 0.9.0 consumption under
  Changed (the stub-sweep audit: no ManagerError/RelayError matching,
  no Establishment construction, no marked-pub surface reached through
  the HTTP API) and the two ride-through fixes under Fixed
  (register_imported Result in the WS overlay tests, fuzz/shared spec
  alignment)

Verification: cargo test/clippy/fmt/doc green in the preceding deps
commit; no source change in this commit
2026-10-14 05:20:13 +00:00
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 2cf0dc7bea docs + clippy: drop the retired C-08 stub reference; cloned_ref_to_slice_refs
- websocket/upgrade.rs: OpenableAlpn's spec doc advertised the removed
  `channel:pub_open_not_implemented` stub (upstream C-08, fixed in
  alkcall with the single-publish fixes) — rewritten to the current
  contract: Pub-typed marked specs are refused loudly at registration
  by `register_openable_with_establisher`; marked pub openables route
  through `ChannelCore::register_pub_openable`. Also dropped the
  stale 'alkcall 0.6.0' version pin
- adapters/from_openapi.rs: clippy rust-1.99
  `cloned_ref_to_slice_refs` — std::slice::from_ref instead of a
  single-element to_string slice

Verified: cargo test (458 ok), clippy --all-targets -D warnings,
fmt --check
2026-10-13 11:32:16 +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 a16dee7385 docs(fuzzing): sse_parser 45-min post-FWD-22 rerun clean
Exited 0 at 2765 s: 96k execs at ~33/s, coverage 1378 edges / 6655
features / 734 corpus, still growing at budget end, zero findings.
Wave 4 is fully done: five of five targets exited 0.
2026-09-29 20:17:06 +00:00
glm-5.3-flash a7d3e3afc3 docs(fuzzing): wave-4 long-campaign results + FWD-22 recorded
45-min release-budget campaigns, all five targets launched
concurrently: ws_chunk_framing / spec_import / ws_session_opseq /
retry_throttle all exited 0 (coverage saturated or still growing at
budget end; details in plan §5). sse_parser aborted at 1913 s on
FWD-22 (harness-assert fix, previous commit); 45-min post-fix rerun
in flight. Corpus merges deliberately deferred.
2026-09-29 19:25:42 +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 4e8a2cfddd docs(fuzz): record wave-3 campaign results + FWD-21 2026-09-29 17:28:23 +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 27478f936b docs(fuzz): wave-2 spec_import campaign clean — 2.34M execs, zero findings 2026-09-29 10:18:21 +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 84c4b48407 fuzz: wave-1 smoke campaigns clean + grow-corpus gitignore fix
Both 10-minute detached campaigns (§5 budget) ran to completion:
sse_parser 18.9k execs / 1100 edges (still discovering at budget end),
ws_chunk_framing 4.8M execs / 245 edges (saturated) — zero crashes,
hangs, OOMs, or leaks on all fork jobs; artifact dirs empty.

One policy gap the first campaigns exposed: the grown corpus entries
(hash-named files in fuzz/corpus/<target>/) leaked as git noise under
the un-ignored corpus dirs. The hash-pattern gitignore now separates
the committed seed-* files from campaign output, per the quinn/h2
model.

docs/plans/fuzzing.md: §3 table marks targets 1-2 implemented; §5
gains the grow-corpus policy + campaign results; §6 sequencing marks
step 1 done. FWD-20 already carried the fix log from 5971c0a.

Verification: corpus replay 80/80 green, cargo test 441 passed, fmt
--check clean.
2026-09-29 09:18:08 +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
glm-5.3-flash 779e3ea908 docs(plans): fuzzing plan for alkhttp (4 targets, sibling pattern)
Adopts the fuzzing pattern proven in alkcall/alktty/alktunnels/
alksocks: fuzz/ workspace with stable-toolchain shared invariant
crate, committed seed corpora, corpus replay as the standing gate,
detached runner for campaigns. Four targets planned — sse_parser and
ws_chunk_framing (this session), spec_import and the stateful wave
(later sessions). §3 table and §6 sequencing track progress.
2026-09-29 07:22:15 +00:00
glm-5.3-flash 40055170a0 build(deps): bump alkcall to 0.8.1
Picks up the duplicate adopt/open collision fix found by alkcall's
manager_routing fuzz campaign (alkcall CHANGELOG 0.8.1): a duplicate
adopt_channel/open_channel no longer replaces the live channel's
state. alkhttp reaches the fixed paths via the shared ChannelManager;
no alkcall API surface used by this crate changed.

Verified: cargo test (456 passed), cargo test --all-features,
clippy --all-targets -- -D warnings, fmt --check.
2026-09-29 06:42:17 +00:00
glm-5.3-flash 911f186629 chore: release alkhttp 0.5.0
- version bump 0.4.1 -> 0.5.0 (minor per the dep-wave convention:
  0.3.0 rode alkcall 0.6.0, 0.4.0 rode alkcall 0.7.0)
- README usage snippet 0.4.1 -> 0.5.0
- changelog: cut [0.5.0] with the alkcall 0.8.0 consumption under
  Changed — the additive-only audit (reply fields, relay/hub-leg,
  flavor-form discovery) and the one ride-through discovery change
  (explicit channel_open_alpn for non-standard open-op names)

Verification: cargo test/clippy/fmt/doc green in the preceding deps
commit; no source change in this commit
2026-09-18 13:48:26 +00:00
glm-5.3-flash a63f4ed2c9 chore(deps): consume alkcall 0.8.0
- bump alkcall 0.7 -> 0.8 (gateway feature unchanged). No code change
  forced: alkcall 0.8.0 is additive on the call-plane JSON and the
  registry seams (review 008's remediation — the establisher reply
  projection, flavor-form open-op ids in discovery, the in-tree
  ChannelRelay + hub-leg install template) and none of the new
  surfaces are constructed in this crate: no Establishment reply
  fields (the WS ferry passes Option<OpenEstablisher> through
  unchanged), no marked specs imported through from_call (the from_wss
  filter excludes the protocol-session ops), no ChannelRelay /
  HubLegImports / HubLegTemplate consumption (hub-leg assembly stays
  at the assembly layer)
- one ride-through on this crate's discovery surface: a deployment's
  with_ws_openable_alpns open-op specs with non-standard names now
  serve an explicit channel_open_alpn string through services/list +
  services/schema (alkcall 0.8.0 U-1, ADR-047 amendment 3) so hubs
  relaying via from_call reconstruct the marker; standard-shape names
  stay byte-stable

Verification: cargo test 456 pass (default) / 589 (all-features),
clippy --all-targets -D warnings clean (default + all-features),
fmt --check clean, doc --no-deps clean
2026-09-18 13:48:23 +00:00
glm-5.3-flash e854947c86 chore: release alkhttp 0.4.1
- version bump 0.4.0 -> 0.4.1 (patch: the MSRV raise is a metadata
  change that breaks no downstream able to build the crate before)
- README usage snippet 0.4 -> 0.4.1
- changelog: cut [0.4.1] with the alkcall 0.7.1 consumption + the
  rust-version 1.85 -> 1.88 floor raise under Changed

Verification: cargo publish --dry-run --allow-dirty (cargo test/clippy/
fmt green in the preceding deps commit)
2026-09-10 03:05:45 +00:00
glm-5.3-flash 660ccf3e76 chore(deps): consume alkcall 0.7.1; raise rust-version floor 1.85 -> 1.88
- the 1.85 claim was already false at the dependency level: the resolved
  lockfile pulls icu_* 2.x (MSRV 1.86 via jsonschema -> idna) and, since
  alkcall 0.7.1, alkcall itself declares 1.88 — 1.85 toolchains cannot
  build the tree regardless of what Cargo.toml declared
- raising the floor to 1.88 aligns with the ecosystem MSRV decision
  (noq QUIC path, per the alknet MSRV audit; recorded for alktls in
  OQ-TLS-08) and breaks no downstream that could build the crate before
- alkcall 0.7.1 rides in via the lockfile (version = "0.7" already
  covered it); no code change forced — 0.7.1 is its own MSRV raise plus
  the cf-006/cf-007 work consumed in the 0.7.0 wave
- one 1.88 clippy lint fixed: uninlined_format_args in a forward.rs test

Verification: 1.85 check fails as documented (icu_* 1.88 floors); 1.88
toolchain check/test/clippy --all-targets -D warnings clean; stable 1.94
test 456 (default) / 589 (all-features), clippy -D warnings (default +
all-features), fmt --check, doc --no-deps
2026-09-10 03:05:27 +00:00
glm-5.3-flash 8832160db1 chore: release alkhttp 0.4.0
- version bump 0.3.0 -> 0.4.0 (the CONNECTION_CLOSED -> 503 gateway
  mapping is a wire-visible behavior change for HTTP callers matching
  on the previously-emitted 500 — a minor bump, not a patch)
- README usage snippet 0.3 -> 0.4
- changelog: cut [0.4.0] with the alkcall 0.7.0 wave entry
  (CF-005/006/007, no code change forced) + the CONNECTION_CLOSED
  mapping under Changed (wire-surface behavior)

Verification: cargo test (456 passed), cargo test --all-features
(589 passed), clippy -D warnings (default + all-features), fmt
--check, cargo publish --dry-run --allow-dirty
2026-09-07 18:11:07 +00:00
glm-5.3-flash dbaaa4ec8c chore(deps): consume alkcall 0.7.0 — CONNECTION_CLOSED maps 503
Dependency wave + one wire-surface behavior fix:

- bump alkcall 0.6 -> 0.7 (gateway feature unchanged). No code change
  forced: this crate constructs no ServingConfig (the one 0.7.0
  struct-literal break — from_wss keeps the pure-consumer
  from_connection; opt-in serving remains the review-006 Part C
  deferred decision), and the CF-006 per-call opener-identity overlay
  is behavior-only upstream (hub-forwarded WS-ferry opens now resolve
  the end client's identity, the intended effect)
- gateway error table maps CONNECTION_CLOSED -> 503 Service
  Unavailable: alkcall 0.7.0's CF-007 amendment makes it the eighth
  protocol code (ADR-016's list is formal at eight); it previously
  fell to the catch-all 500, misreporting a retryable,
  provably-undelivered call as a permanent server failure. retryable:
  true, so the retry_after details path applies on 503. Tests pin the
  mapping and the Retry-After passthrough
- ADR-023 amended: the protocol-code list is eight (ALREADY_EXISTS +
  CONNECTION_CLOSED rows added to the §3 table, normative rule and
  call.error code list updated); http-adapters.md six-code references
  de-staled

Verification: cargo test (456 passed), cargo test --all-features
(589 passed), clippy -D warnings (default + all-features), fmt
--check, cargo doc --no-deps (0 warnings)
2026-09-07 18:01:36 +00:00
glm-5.3-flash 126fdec243 chore: release alkhttp 0.3.0
- version bump 0.2.0 -> 0.3.0 (the alkcall 0.6.0 consumption wave
  carries a compile-breaking change for downstream users of
  `OpenableAlpn::new`: handler closures gain the `_plan` parameter,
  so this release cannot be a patch)
- README usage snippet 0.2 -> 0.3
- changelog: cut [0.3.0] with the alkcall 0.6.0 wave entry
  (OpenHandler plan parameter, Establishment.plan threading,
  pump_bidi availability)

Verification: cargo test (454 passed), cargo test --all-features
(587 passed), clippy -D warnings (default + all-features),
cargo fmt --check, cargo doc --no-deps,
cargo publish --dry-run --allow-dirty
2026-09-07 09:37:38 +00:00
glm-5.3-flash a9429dfb1d chore(deps): consume alkcall 0.6.0 — OpenHandler gains the plan parameter
Mechanical pass for the alkcall 0.6.0 bump (review 007's
establishment follow-ups sweep: R-01 Establishment plan payload,
R-02 OpenHandler lifetime doc note, R-03 pump_bidi extraction):

- bump alkcall 0.5 -> 0.6 (gateway feature unchanged)
- the OpenHandler signature gained a plan parameter —
  Fn(Value, Option<ChannelPlan>, Connection, AuthContext) ->
  JoinHandle<()> — so the test's echo_open_handler closure gains
  `_plan` (the only closure-construction site in this crate)
- no ferry change: this crate constructs no Establishment and passes
  the Option<OpenEstablisher> through unchanged (OpenableAlpn fields
  and register_openable_with_establisher threading are unchanged in
  0.6.0); the establisher's Establishment.plan now reaches the pump
  handler's second parameter process-locally (ADR-049 amendment 2)
- docs updated: websocket.md (plan threading + pump_bidi pointer),
  OpenableAlpn field docs, with_ws_openable_alpns doc comment
- CHANGELOG [Unreleased] consumption-wave entry

Verification: cargo test (454 passed), cargo test --all-features
(587 passed), clippy -D warnings (default + all-features),
fmt --check, cargo doc --no-deps clean
2026-09-07 09:24:00 +00:00
glm-5.3-flash 0b60b8d0be chore: release alkhttp 0.2.0
- version bump 0.1.0 -> 0.2.0 (new public API: OpenableAlpn
  establisher fields + with_establisher builder; rides alkcall
  0.5's semver-incompatible channels types)
- README usage snippet 0.1 -> 0.2
- changelog: cut [0.2.0], add the missing alkcall 0.5.0 wave
  (ADR-049 establisher pass) to the dependency history

Verification: cargo test (454 passed), cargo test --all-features
(587 passed), clippy -D warnings (default + all-features),
cargo fmt --check, cargo doc --no-deps,
cargo publish --dry-run --allow-dirty
2026-09-06 20:10:12 +00:00
glm-5.3-flash 80d6ba0e5a feat(websocket): ride alkcall 0.5.0 — OpenableAlpn establisher + timeout (ADR-049 Unit 3)
Mechanical pass for the alkcall 0.5.0 bump (review 006 sequencing:
ADR-049 → alkcall 0.5.0 → alkhttp mechanical pass):

- bump alkcall 0.4 → 0.5 (gateway feature unchanged)
- OpenableAlpn gains establisher: Option<OpenEstablisher> and
  establisher_timeout: Option<Duration> (both default None via
  OpenableAlpn::new — existing constructions compile unchanged) plus
  a with_establisher(establisher, timeout) builder
- the install_channel_zero ferry calls
  register_openable_with_establisher, threading both fields through
- docs updated (websocket.md, adapter.rs doc comments)

The alkcall 0.5.0 open_channel error-type change (ADR-049 §4) needs
no change here — this crate never calls open_channel (from_wss uses
ChannelClient::from_connection only).

Verification: cargo test (454 passed), cargo test --all-features
(587 passed), cargo clippy --all-targets/-D warnings (default +
all-features), cargo fmt --check
2026-09-06 20:04:25 +00:00
glm-5.3-flash 5f12abf80e chore: drop dead Cargo.lock exclude entry
cargo always packages the lockfile for libraries (since 1.62), so
the exclude entry did nothing. Package contents unchanged.
2026-09-05 18:22:34 +00:00
glm-5.3-flash ce6c29a6c8 fix(features): client implies openapi, per the spec
AGENTS.md, lib.rs, the README, and adapters/mod.rs docs all state
openapi is the shared spec model implied by both feature sides, and
the README's client row claims from_openapi under client — but the
feature graph only had server -> openapi. A lean client-only build
compiled yet silently lacked from_openapi (gated all(client,
openapi)).

Verification: cargo test (default + all-features), clippy both
feature sets -D warnings, fmt, publish --dry-run — all green.
2026-09-05 18:04:56 +00:00
glm-5.3-flash d08ea44ad1 docs(changelog): record the alkcall 0.3 -> 0.4.1 consumption waves
The [Unreleased] section still led with the alkcall 0.2 bump; the
0.3 (per-session fork, op/register, UP-03), 0.4.0 (registry
input_schema enforcement), and 0.4.1 (early-arrival chunk parking)
waves were unrecorded. Also removes a copy-paste duplication in the
CF-003 entry.

Verification: cargo test, clippy (both feature sets), fmt, doc,
publish --dry-run — all green.
2026-09-05 18:03:03 +00:00
glm-5.3-flash a131325d28 chore: alkcall 0.4.0 -> 0.4.1 (early-arrival park fix) 2026-09-05 07:05:28 +00:00
glm-5.3-flash 1ce886b9d4 chore: alkcall 0.3 -> 0.4.0 (registry input_schema enforcement)
alkcall 0.4.0 now enforces OperationSpec.input_schema at dispatch
(INVALID_INPUT). All 438+16 tests pass; the gateway path is
unaffected because alkhttp's imported ops validate through their own
CompiledInputSchema closure, which composes with the new registry
check (registry = raw JSON Schema semantics, adapter = closed by
default).
2026-09-05 06:34:25 +00:00
glm-5.3-flash 2e15204aca fix(docs): resolve the 5 pre-existing rustdoc warnings
cargo doc --no-deps was not actually clean before review 007's
remediation — it emitted 5 warnings that went uncounted (grep for
"^error|^warning" in the gate runs matched only on error/warning at
line start, and the review baselines recorded "clean" without a
counting check; the warnings were first noticed while re-verifying
the review-007 gate set). All pre-existing, none introduced by
review 007:

- src/adapters/mod.rs: the bare [`OpenAPISpec`] intra-doc link did
  not resolve (the re-export sits in the same module, but rustdoc
  resolves module-level links against the module's own scope) —
  spelled out as `openapi_spec::OpenAPISpec`
- src/websocket/mod.rs: [`upgrade`] did not resolve from the
  `websocket` module doc (the submodule is not in scope for the
  parent's doc links) — plain `upgrade`
- src/websocket/upgrade.rs:215: [`CallConnection`] had no import in
  scope — spelled out via the full alkcall path
- src/websocket/upgrade.rs:291: [`install_channel_zero`] linked a
  private item from public docs — plain `install_channel_zero` hook
- src/server/adapter.rs:274: [`crate::websocket::OpenHandler`] is
  not re-exported from `websocket` — spelled out via the alkcall
  path

Verification: cargo doc --no-deps 0 warnings (was 5); cargo test 454
passed / 0 failed; cargo test --all-features 587 passed / 0 failed;
clippy (both configs) clean; fmt clean.

Review: docs/reviews/007-ws-data-channel-surface-review.md (the
baseline's "cargo doc → clean" claim was wrong on this dimension)
2026-09-05 05:59:54 +00:00
glm-5.3-flash 802d94ec07 docs(review 007 Unit 2): WS-31 discovery-shadowing note + record corrections
- WS-31: websocket.md §"Data channels for browsers" + ADR-067's landed
  note record that WS-session discovery is the bootstrap set — the
  hook's bootstrap `services/*` registrations overwrite a
  base-registry `services/*` registration on the WS path by design
  (a deployment's custom `services/list` is shadowed on WS sessions
  only).
- ADR-048's landed note: correction + completion — the WS-26
  retention sentence was aspirational at the landed commit (WS-28) and
  is now real; the UP-02 posture's override half is now an explicit
  surface (`with_ws_op_register_acl` / `OpRegisterAcl`), with the
  note that `ChannelsPolicy` could not carry an op ACL.
- ADR-067's landed note: review-007 notes (WS-28 fix + gate, WS-29
  surface, WS-31 record).
- OQ-05 resolution: the retention claim carries the WS-28 correction.
- review-006 UP-02 log + WS-26 paragraph: corrections marking what the
  pre-fix tree did not have, with the landed remediation named.
- review-002 WS-17: the "bounded at 64 sessions" claim corrected —
  the bare-registry semaphore was per-request and bounded nothing;
  `SessionSlots` is the shared-cap surface.
- review-007 status: open for remediation → remediated, with the
  decisions taken (both "implement" options) and the gate names.

Verification: cargo test 454 passed / 0 failed; cargo doc --no-deps
clean (6 pre-existing warnings, identical at baseline).

Review: docs/reviews/007-ws-data-channel-surface-review.md
2026-09-05 05:45:31 +00:00
glm-5.3-flash 24c2e9a224 feat(websocket): WS-29/30/32 — op/register ACL surface, SessionSlots, builder-path gate
Review 007 Unit 2 (the two "implement" decisions taken during
remediation, plus the coverage gap):

- WS-29: the `op/register` override surface review-006 UP-02 and
  ADR-048 recorded as landed is now implemented. The hook threads an
  `op_register_acl` `AccessControl` into `op_register_spec` (the
  permissive `AccessControl::default()` remains the default
  everywhere); the built-in surface sets it via
  `HttpAdapter::with_ws_op_register_acl`, bare-registry/custom routes
  via the `OpRegisterAcl` request extension (mirroring
  `ChannelsPolicy`/`WsTimeouts`/`OpenableAlpns`). A peer whose
  identity does not satisfy the ACL gets `FORBIDDEN` on the announce.
  Gates: builder path (`FORBIDDEN` scope-less / announce-ok scoped)
  + extension path.

- WS-30: the bare-registry `SessionState` is built by `FromRef` per
  request, so its default-cap semaphore bounds nothing across
  requests (corrects review-002 WS-17's "bounded at 64 sessions"
  claim). New `SessionSlots` request extension carries the shared
  semaphore for routes that need an effective cap; the upgrade
  handler prefers it over the state value. Doc comments corrected
  (`SessionState`, `WsTimeouts`, `ws_upgrade_handler`). Gate:
  cap-1 route → 503 over cap → slot freed on session end.

- WS-32: the built-in openables threading
  (`with_ws_openable_alpns` → `RouterState` → `SessionState` → hook)
  gets its first gate — every Unit-3 gate rode the `OpenableAlpns`
  extension fallback. `builder_path_openables_serve_the_data_channel_
  surface` discovers the openable via `services/list`, opens the
  channel, and round-trips bytes through the builder-built router.

Verification: cargo test 454 passed / 0 failed; cargo test
--all-features 587 passed / 0 failed (+5 gates); clippy (both
configs) clean; fmt clean.

Review: docs/reviews/007-ws-data-channel-surface-review.md
2026-09-05 05:44:28 +00:00
glm-5.3-flash 38738943c7 fix(websocket): WS-28 — bind the channel-0 ConnectionGuard in the task's frame
The WS-26 `ConnectionGuard` was bound inside its `if let` block, so it
dropped microseconds after insertion instead of living for the
channel-0 dispatcher task — `live_connections()` /
`live_connection_count()` were permanently empty for every session
(review 007 WS-28 [major], reproduced empirically; the handle
retention ADR-048 + review-006 record was aspirational at that commit).

Remediation (review 007 Unit 1):

- bind the guard as an `Option<ConnectionGuard>` in the channel-0
  task's frame, mirroring `SessionGuard`'s shape in
  `run_channels_session`; the block comment now describes the real
  scope
- gate `live_connections_visible_mid_session_and_drain_after_teardown`
  — handle visible mid-session (after a completed call proves the
  dispatcher is up), drained after teardown; verified to fail against
  the pre-fix tree and pass with the fix

Verification: cargo test 454 passed / 0 failed; cargo test
--all-features 582 passed / 0 failed; clippy (both configs) clean; fmt
clean.

Review: docs/reviews/007-ws-data-channel-surface-review.md (WS-28)
2026-09-05 05:41:53 +00:00
glm-5.3-flash b36ec49cc1 docs(review 007): fresh-eyes pass over the landed WS data-channel surface
First review whose subject is the landed wiring itself (review 006
Units 2-4, 030c5ef/2053420/64fa10b), not the gap to it.

Findings:
- WS-28 [major] — the WS-26 ConnectionGuard is bound inside the
  `if let` block and drops when it ends, not when the channel-0 task
  ends: live_connections() is permanently empty for every session.
  Reproduced empirically through the live WS path (scratch test,
  run + removed). ADR-048's landed note and review-006's log record
  behavior the code does not have.
- WS-29 [minor] — the op/register ACL override recorded as landed
  (review-006 UP-02, ADR-048) is not implemented; the hook hardcodes
  AccessControl::default() and ChannelsPolicy cannot express an op ACL.
- WS-30 [minor] — the bare-registry default session cap is built per
  request (FromRef) and bounds nothing; corrects review-002 WS-17.
- WS-31 [minor] — install_bootstrap_discovery silently shadows a
  deployment's own services/list on WS sessions (upstream-mandated,
  unrecorded).
- WS-32 [minor] — the router-state openables threading
  (with_ws_openable_alpns) has no gate; every Unit-3 gate rides the
  extension fallback.

Plus non-findings bounding the re-review (UP-01 arm, policy threading,
from_wss exclusion set, extension precedence, gate fidelity) and a
sequenced remediation plan.

Baseline at 64fa10b: 454 / 582 tests, clippy both, fmt, doc — all clean.
2026-09-05 00:46:22 +00:00
glm-5.3-flash 64fa10be31 docs(review 006 Unit 4): spec reconciliation — OQ-05 resolved, WS data-channel docs to the landed state
- OQ-05: deferred → resolved (2026-09-04, review 006 Unit 2+3); the
  consumer-set reframe recorded (WS is also the native-client fallback
  behind hostile NAT/firewall; OQ-04 does not block the wiring).
- ADR-067: status amendment + the v1-cut blockquote gains the Wired
  (2026-09-04) note — per-session-fork shape, openable surface, gates.
- ADR-048: landed-state amendment — §4's hub→browser direction has its
  object (op/register → connection overlay, hub composes via the
  retained Arc<CallConnection>); the op/register ACL posture (UP-02,
  SRV-10 precedent) recorded.
- websocket.md: the step-7 deferral note and the §"Data channels for
  browsers" status block removed (the section now documents the landed
  surface: with_ws_openable_alpns, the OpenableAlpns fallback, cap
  policy, discovery, gates); idle-knob deployment note for silent data
  channels (semantics unchanged; the 60 s default bites more often —
  set None at assembly for long-lived interactive channels).
- Review 003 status → remediated (all findings closed; log in review
  006); its Unit-4 section marked landed.
- alknet-ADR-044 §5 pointer checked: not stale.

Verification: cargo test 454 / 0; --all-features 582 / 0; clippy
(both) clean; fmt clean; doc clean.
2026-09-04 16:17:11 +00:00
glm-5.3-flash 2053420f7d chore(deps): consume alkcall 0.3.1 — list-peers announced-op discovery gate
The alkcall 0.3.1 fix (fd21230, alkhttp review 006 UP-03) lands
PeerCompositeEnv::peer_operations, so services/list-peers now lists
peer-announced ops. Version pin stays 0.3 (semver-compatible); the
lockfile bump carries no source change.

- Extend op_register_served_per_session_and_collision_is_already_exists
  with the discovery assertion that originally surfaced UP-03: after
  the announce resolves, services/list-peers attributes consumer/exec
  to the alice peer entry (fails against 0.3.0, verified by the
  original draft).
- Review 006 UP-03 entry records the 0.3.1 landing (alkcall ADR-030).

Verification: cargo test 454 passed / 0 failed; cargo test
--all-features 582 / 0; clippy (default + all-features, all-targets,
-D warnings) clean; fmt clean; doc clean.
2026-09-04 16:17:07 +00:00
glm-5.3-flash 214fd213ff docs(review 006): remediation log for Unit 2+3; UP-03 filed upstream-side
- Unit 2/Unit 3 recorded as landed (030c5ef): hook rework shape,
  policy threading (the ChannelsPolicy seam the ledger gate caught),
  WS-26 handle retention, from_wss protocol-op exclusion, UP-01, the
  six new gates, and the harness hardening notes (request-id filter,
  flat CallError payload shape).
- UP-02 decided: op/register serves per session with the default ACL
  (SRV-10 posture); the visibility tension resolves as ACL-gate.
- UP-03 [minor, alkcall-side] filed: services/list-peers cannot list
  peer-announced ops — PeerCompositeEnv overrides peer_ids but not
  peer_operations (trait default Vec::new()), so the amendment's
  peer-discovery promise does not resolve on the wire. Discovered by
  the gates; fix lives in alkcall; alkhttp's gate asserts
  overlay-landing + collision semantics instead and is not blocked.
- Unit 4 (docs reconciliation) remains open.

Verification unchanged: cargo test 454/582, clippy both sides, fmt,
doc — clean.
2026-09-04 15:47:48 +00:00
glm-5.3-flash 030c5efa51 feat(websocket): data-channel + op/register wiring (review 006 Unit 2+3)
The WS path wires the alkcall 0.3 per-session mechanisms — the OQ-05
deferred half (review-003 WS-20/21/22/25/26, the decisions WS-24/WS-25
resolved upstream):

- install_channel_zero reworked per the ADR-047 §4 amendment #2 shape:
  fork the deployment's base registry, register the generic channel
  ops (channel/close, channel/control, channel/resources/subscribe —
  WS-21), the deployment's openable ALPNs (ChannelCore::register_openable,
  WS-22), the bootstrap discovery set closed over the fork
  (install_bootstrap_discovery, F-06), and op/register (WS-25; the
  collision set is the fork per review-005 G-03), then dispatch over
  the fork. The session's ChannelsPolicy rides the hook (one policy
  instance across open wrappers and the demux teardown path).
- OpenableAlpn { spec, open_handler } + HttpAdapter::with_ws_openable_alpns,
  threaded RouterState -> SessionState -> hook, with the OpenableAlpns
  request-extension fallback (mirroring ChannelsPolicy/WsTimeouts).
- WsSessions retains the channel-0 Arc<CallConnection> (WS-26) with a
  self-removing guard (ConnectionGuard); live_connections() is the
  deployment-visible surface.
- UP-01: ALREADY_EXISTS maps to 409 Conflict in the gateway error map.
- from_wss import excludes the protocol-session ops (bootstrap set +
  channel lifecycle ops): the fork serves them per session, and proxying
  session-scoped machinery (e.g. channel/close across sessions) would be
  nonsense. Discovery runs first, the filter is the listing minus those
  names.
- adapter_install_channel_zero cfg matches its caller (WS-27); it
  inherits the reworked hook (session ops now served in the from_wss
  test-server producer too).

Gates (Unit 3, tests/ws_upgrade_session.rs; the WS-23 e2e set):
open -> channel_id -> discoverable in services/list -> chunks both
ways -> handler sees bytes; channel/close resolves + ledger decrement;
cap denial (channel:-prefixed); mid-open disconnect teardown; TooLarge
demux resync through the WS path (16 MiB + 1 skip consumed);
op/register announce + overlay-collision + serving-registry-collision
ALREADY_EXISTS through the WS path.

call_and_await now filters by request id and tolerates data-channel
chunks (a prior Sub's trailing call.completed may interleave).

Verification: cargo test 454 (default) / 582 (all-features), clippy
both sides -D warnings clean, fmt clean, doc clean.
2026-09-04 15:47:15 +00:00
glm-5.3-flash 90790374fa docs(review 006): alkcall 0.3.0 consequence review — WS-24/25 resolved upstream, residual enumerated
Records the review pass of alkcall 0.3.0 against this crate:

- WS-24 (dispatch-resolution mechanism) and WS-25 (client-side op
  registration) verified resolved upstream — per-session fork (alkcall
  ADR-047 §4 amendment #2) + op/register (alkcall ADR-022 amendment
  2026-09-03 + 2026-09-04 collision sub-amendment); the G-01 serving-
  loop concurrency rework lands here via the bump alone.
- One new finding: UP-01 [minor] — ALREADY_EXISTS (new 0.3 protocol
  code) unmapped in the gateway error table (would 500); fix rides
  Unit 2. UP-02 records the op/register ACL posture decision for
  Unit 2.
- WS-20/21/22/26/27/23 re-verified still open, now unblocked; the
  remediation plan re-sequences (Unit 2 wiring with the concrete
  fork shape, Unit 3 gates + two upstream-mirroring additions,
  Unit 4 docs).

Baseline gates at df86f89: cargo test 453/575, clippy both sides,
fmt, doc — all clean.
2026-09-04 14:58:43 +00:00
glm-5.3-flash df86f89440 docs(review 003): land the Unit-1 re-point; extend it to alkcall review 005 + 0.3.0
Commits the re-point edits left uncommitted in the working tree
(5b62307's follow-through; alkcall review 005 verified these lines at
that tree), updated to the post-review-005 state:

- ADR-048 reconciliation note gains the 2026-09-04 update: alkcall
  review 005 remediated the landed mechanisms (serving-loop
  concurrency G-01/G-02, op/register collision policy G-03, spec
  round-trip G-04, bootstrap-list alignment G-05) and alkcall 0.3.0
  shipped them; alkhttp now consumes 0.3. The ADR-022 collision
  sub-amendment binds here: a peer-announced op never shadows the
  serving side's own registrations — the WS session's op/register
  handler gates on the session fork.
- OQ-05 resolution gains the same dated update and extends the
  cross-references to alkcall reviews 004-005.

What remains here is still alkhttp-side wiring only (review 003
Unit 2).
2026-09-04 14:55:23 +00:00
glm-5.3-flash 3dee46aead chore(deps): consume alkcall 0.3 — per-session fork, serving loop, op/register
Bump alkcall 0.2 -> 0.3 (published 2026-09-04; the remediation of
alkcall reviews 004-005, the upstream resolution of alkhttp review 003
WS-24/WS-25). Two source-break classes, both mechanical:

- take_call_connection now returns Option<Arc<CallConnection>>
  (was bare value) — drop the double-wrap in WssSession::connect.
- register/register_on/register_openable take &OperationRegistry
  (was &mut) — drop 51 stale `mut` bindings (clippy --fix), which is
  the interior-mutability change that makes the per-session fork
  surface possible.

No behavior change in this crate yet; the 0.3 mechanisms (fork,
serve_single_stream, op/register, install_bootstrap_discovery,
collision policy) are the composition surface for alkhttp review 003
Unit 2 (WS-20..22, WS-26 wiring) — not wired here yet.

Verification: cargo test 453 passed (default) / 575 passed
(all-features), clippy both sides -D warnings clean, fmt clean,
cargo doc --no-deps clean.
2026-09-04 14:55:19 +00:00
glm-5.3-flash 5b62307be9 docs(review 003): Unit 1 re-pointed at alkcall review 004; bootstrap-op candidate recorded
The alkcall decision work (WS-24 dispatch-resolution mechanism, WS-25
client-side op registration) now lives upstream as alkcall review 004.
Unit 1 records the leading candidates and their alkhttp consequences:

- WS-24 -> option (a), per-session base registry (the only
  wire-proven dispatch shape; fork surface = alkcall F-02)
- WS-25 -> channel-0 bootstrap op (op/register as an assumed op in
  the bootstrap set; requires the alkcall client serving half, F-04)
- discovery composition: services/list on the fork + peer-registered
  ops via the already-built services/list-peers

Consumer-set reframe from WS-23 unchanged; no findings added or
removed.
2026-09-03 14:42:50 +00:00
glm-5.3-flash 19e30c9769 docs(review 003): WS data-channel wiring gap drill-down
Focused review of the OQ-05 deferral (review-001 WS-03): what is
missing to wire browser/native data channels over WS, the deferral
rationale check, and the design gaps found behind the 'nothing new
to design' assumption.

Findings (continuing review-002 numbering):
- WS-20..22 [major]: mechanical wiring gap — install_channel_zero
  discards the ChannelManager, generic channel lifecycle ops never
  registered, no openable-ALPN deployment surface
- WS-23 [minor]: no browser-opened-channel e2e test; OQ-05 reopen
  trigger reframed (native WS consumers are not blocked on OQ-04)
- WS-24 [major, cross-crate]: top-level dispatch never consults the
  connection overlay — the ADR-047 §4 amendment mechanism cannot
  resolve open ops on the wire (needs an alkcall decision)
- WS-25 [major]: no wire mechanism for client-side op registration —
  ADR-048 browser-side bidirectionality is undesignable as specified
- WS-26 [major]: no retained live connection handle; hub cannot
  reach a session's overlay or push toward it
- WS-27 [minor]: dead-code-gated test hook cleanup

Includes a non-findings section (channel-id split, demux hardening,
open-op wrapper completeness, idle-knob posture) and a 4-unit
remediation plan sequenced so the alkcall decision task (Unit 1)
gates the alkhttp wiring shape (Unit 2).

Verification: cargo test (453) / --all-features (575), clippy
(all-targets, both feature sets), fmt — all clean at 58665f2.
2026-09-03 08:31:44 +00:00
glm-5.3-flash 58665f2061 docs: add crate README, MIT/Apache license files
- README covering the gateway surface, both import/export adapter
  families, feature flags, and security posture (all claims verified
  against the current code)
- standard MIT and Apache-2.0 license texts matching the
  license = "MIT OR Apache-2.0" manifest field
- Cargo.toml: point the manifest at README.md and exclude scripts/
  from the published package
2026-09-01 11:18:36 +00:00
glm-5.3-flash a80f9948b8 feat(build): feature-sided builds — server/client sides independently selectable (ADR-039 Amendment 1)
Split the feature graph so consumers pulling only the import adapters
(from_openapi / from_jsonschema / from_mcp) no longer compile the axum
/ hyper server stack, and server-only deployments no longer compile
reqwest. One crate, one import path — sides cut by features, not by a
crate split.

Feature graph:
- server (default): axum host, gateway, WS upgrade, to_openapi, to_mcp
- client (default): client host, forward, from_jsonschema, from_openapi
- openapi: shared OpenAPISpec model (implied by both sides)
- mcp: from_mcp needs client, to_mcp needs server
- wss: tungstenite transport (from_wss); tungstenite half of the
  shared WS↔byte-stream adapter
- h2/http1: hyper protocol features; imply server

Wire-contract neutral: gateway endpoints, ALPNs, and all public API
shapes unchanged; defaults keep both sides on.

Supporting changes:
- forward.rs drops its axum::body::Bytes type leak (bytes crate types)
- bounded_join + error-echo caps move to input_validation (usable by
  both sides; openapi_spec no longer imports from forward)
- byte_adapter: axum flavor compiles under server, tungstenite under
  wss; the generic pumps stay shared (WS-11)
- input_validation / openapi_spec import-only internals gated to the
  side that consumes them
- http-body-util moves to dev-dependencies (was test-only)
- integration-test required-features updated for the new sides
- from_wss unit tests (axum producer harness) gated to server

Verified: cargo test (defaults, 453) and --all-features (575) pass;
lean side builds (client / server / client,mcp / client,wss /
server,wss / openapi-only) build clean with zero warnings;
clippy -D warnings clean across all feature combinations; fmt clean.
2026-08-31 17:19:05 +00:00
glm-5.3-flash 8e8e1f2b14 refactor(gateway): migrate to alkcall 0.2 promoted gateway module
Bump the alkcall dependency to 0.2 (with the gateway feature) and
converge on the promoted shared pieces:

- The local dispatch spine (gateway/dispatch.rs, 721 lines) is deleted;
  GatewayDispatch, schema_disclosure_denial, and DEFAULT_DEADLINE are
  re-exported from alkcall::gateway (alkcall ADR-048). The 30 s default
  deadline preserves the previous behavior exactly.
- gateway/schema_cache.rs (PublishSchemaCache) is deleted: alkcall CF-003
  compiles publish_schema at registration time and exposes
  OperationRegistry::publish_validator; the /publish chunk stream
  resolves against it. Un-compilable schemas are now rejected at
  registration, so the two end-to-end fail-closed tests were reworked
  into a registration-rejection test (a stronger guarantee).
- schema_disclosure_denial consumers (to_mcp, routes) use alkcall's
  promoted implementation; the alkhttp-local copy is gone (ADR-071
  updated: the guard stays as defense-in-depth, the implementation no
  longer forks).
- CF-001: from_wss drop monitor and the WS overlay tests use
  CallError::connection_closed; the review-001-ws-eof-signal race tests
  now assert retryable CONNECTION_CLOSED on both resolution paths (the
  tolerated non-retryable INTERNAL write-failure outcome is gone).
- Added CHANGELOG.md (Keep a Changelog), Unreleased section records the
  bump and convergence.

Verification: cargo test default 453 ok, wss 470 ok, mcp 526 ok,
all-features 575 ok; clippy -D warnings clean (default + all-features,
all-targets); fmt clean; cargo doc warning-free.

Net: -1093 lines.
2026-08-31 10:36:32 +00:00
glm-5.3-flash 2ec02fd578 feat(adapters): enforce advertised input schemas at call time (OAI-18, option a)
Decision: advertise == enforce. The key allowlist (OAI-02) stays as the
first gate with its established unknown-key message; a compiled leaf
validator now runs second, so required/type/enum/pattern/bounds
violations surface as INVALID_INPUT 422 naming the keyword — not as
upstream round-trips.

- new src/adapters/input_validation.rs: CompiledInputSchema compiles an
  op's input_schema once at import with the jsonschema crate (same
  2020-12 dialect publish_schema uses) and validates peer input at call
  time; the compile-time copy is hardened closed-by-default
  (additionalProperties: false injected when absent) so the validator
  reproduces the allowlist's unknown-key semantics; explicit
  additionalProperties:true catch-all and schema values are preserved;
  the original spec value is never mutated
- from_openapi/from_jsonschema import(): compile per registration,
  capture the validator in the handler closure (re-import recompiles —
  the closure capture is the invalidation story); a non-compilable
  input schema fails import loudly (AdapterError::SchemaParse naming
  the operation), matching the publish_schema fail-closed precedent
- from_openapi generated input schemas now carry
  additionalProperties:false explicitly, so the /schema advert states
  the enforced rule and external schema-driven validators reach the
  same verdicts
- forward/forward_stream/build_request gain an
  Option<&CompiledInputSchema> parameter; enforcement runs after the
  allowlist
- round-trip test (review 002 Test-gap 10): the /schema-exported
  input_schema is compiled with the same validator and driven against
  build_request over a 10-input violation matrix — accept-sets exactly
  equal in both directions; the chain-test that lets advertise/enforce
  drift surface as a CI failure
- ADR-066: new decision section (advertise==enforce) with the trust-
  boundary reasoning and the rejected option (b) rationale
- module + enforce_input_schema docs updated to the two-gate shape

cargo test --all-features 596 pass; clippy --all-features/-D warnings,
fmt, doc gates clean.

docs(tasks): mark review-002-fu-oai18-decision done
2026-08-31 07:18:53 +00:00
glm-5.3-flash 7294d19fc7 test(infra): cover review-002 stream/PEM/cap error arms + drop dead WsTimeouts Default
- forward_stream build-error arm (forward.rs): wire-level test asserts
  one INVALID_INPUT envelope then stream end with zero upstream
  contact (a panicking responder counts as the contact guard), plus
  the from_jsonschema integration mirror (undeclared key + non-scalar
  placeholder, each naming its rejection source)
- PEM read-failure arms (http_client.rs): nonexistent CA path →
  CaBundleRead (sync new), nonexistent client-cert path → ClientCertRead
  (async reload, prior generation retained)
- Over-cap poll_write rejection leg (byte_adapter.rs): cap+1 write →
  InvalidData naming the cap; stream stays usable for an at-cap write
  afterwards
- SSE parser edges: CRLF split across feed chunks frames one line;
  invalid-UTF8 data lines drop without killing the frame stream
- from_value structural rejects: non-object doc, missing `info`,
  missing `paths`, non-object `paths` each name the member
- Connection-failure arms: accept-path ConnectionClosed →
  HandlerError::ConnectionClosed via stream_error_to_handler; read-pump
  demux-gone break ends the pump when the byte-stream side is dropped
- Delete the caller-less `impl Default for WsTimeouts` (the extension
  is constructed explicitly)

cargo llvm-cov --all-features: all named arms covered; TOTAL regions
94.18% (was 93.86%), lines 96.04% (was 95.77%); http_client.rs
86.56% lines (was 81.72%).

docs(tasks): mark review-002-fu-stream-error-coverage done
2026-08-31 06:47:29 +00:00
glm-5.3-flash 4b6507c452 docs(websocket,gateway): fix rustdoc link warnings, restore -D warnings doc gate
- dispatch.rs: qualify [invoke]/[invoke_streaming] as
  [GatewayDispatch::invoke]/[::invoke_streaming] (module-level docs
  need the type-qualified path)
- byte_adapter.rs: drop redundant explicit link targets for
  DEFAULT_WS_WRITE_TIMEOUT / DEFAULT_WS_IDLE_TIMEOUT (already
  re-exported at crate::websocket)
- from_wss.rs, byte_adapter.rs: backtick WssSession::drop and the
  private WsFraming (all-features-only link errors, same class)
- Ws pump/timeout module docs spot-checked against WS-13/18/17 impls:
  no drift found

RUSTDOCFLAGS="-D warnings" cargo doc --no-deps now exits clean both
default-features and --all-features.

docs(tasks): mark review-002-fu-doc-warnings done
2026-08-31 06:23:30 +00:00
glm-5.3-flash 5acc561eca docs(tasks): re-validate bracketed follow-up into 3 tasks (OAI-18 decision, streaming-coverage residue, doc-gate fix)
The bracketed planning task's five candidates, re-validated against
the post-bulk tree (0a932e5, all 23 bulk tasks done, 446/568 green):

1. OAI-18 -> review-002-fu-oai18-decision: still key-allowlist-only;
   bulk blockers (yaml/path-item) both landed. Task records the
   enforce-vs-scope-the-advert decision framework from review 002.
2. CON-08/CON-09 close() lever: deferred still (documented v1
   contracts unchanged, no consumer pressure) — not tasked.
3. Cross-crate: CF-001..004 all open in alkcall, no alkhttp-side task;
   PRJ-16 guard already documents its defense-in-depth re-scope
   disposition (no work when CF-004 lands).
4. Stale-comment sweep: mostly absorbed by the bulk (SRV-11 comment
   rewritten in-fix); residue = 3 cargo-doc warnings + WsTimeouts
   redundant Default -> review-002-fu-doc-warnings.
5. ADR-045 version audit: clean (1.4.0 + annotations + test pin).

Post-bulk coverage re-pass (95.89% regions, bulk's new code fully
covered: PRJ-16 guard, body cap, router reorder, batch cap, WS
knobs, OAI-11 node budget all exercised) surfaced one unanticipated
residue -> review-002-fu-stream-error-coverage (forward_stream
build-error arm never exercised on a Sub op; PEM read-failure arms;
over-cap poll_write; SSE split-CRLF edge; from_value structural
rejects; WsTimeouts Default).

Bracketed pass itself marked completed (planning consumed).
taskgraph: 69 valid, no cycles; 3 pending.
2026-08-31 05:40:46 +00:00
glm-5.3-flash 0a932e5ec3 chore(tasks): mark final wave completed (prj16, gw16, cov-knobs, import-loudness, client-policy-wire-tests) 2026-08-31 02:03:50 +00:00
glm-5.3-flash 091518ed7d Merge branch 'wt/review-002-client-policy-wire-tests' 2026-08-31 01:56:11 +00:00
glm-5.3-flash 3b2a26e021 Merge branch 'wt/review-002-cov-deployment-knobs'
# Conflicts:
#	src/gateway/routes.rs
2026-08-31 01:55:12 +00:00
glm-5.3-flash b224531a61 Merge branch 'wt/review-002-import-loudness-cluster' 2026-08-31 01:53:53 +00:00
glm-5.3-flash 59d77360cc Merge branch 'wt/review-002-gw16-status-drift'
# Conflicts:
#	src/gateway/dispatch.rs
2026-08-31 01:52:37 +00:00
glm-5.3-flash 2d53957f08 docs(architecture): record the loud unsupported-feature matrix in http-adapters spec (review 001 OAI-06 + review 002)
The OAI-06 matrix lived only in the completed review-001 task notes;
acceptance for the review-002 loudness cluster requires the successor
doc section. New 'Loud unsupported-feature handling' section on
from_openapi: refused/warned/projected feature tables covering cookie
params, style/explode forms, servers, webhooks, callbacks, security,
oneOf requestBodies, unresolvable or content-less requestBody refs,
path-template validation, collision rejection, ref-sibling warns,
discriminator/xml warns, error-projection mappings, and the OAI-17
error-bounding contract.
2026-08-31 01:47:30 +00:00
glm-5.3-flash 903a91f1d2 fix(adapters): reject header params colliding with default_headers/credential headers (OAI-19)
build_request inserts header params first, then default_headers, then
credential headers — HeaderMap::insert replaces, so a declared
in: header parameter whose name matched a default or credential header
silently never delivered the peer's value upstream.

check_header_param_collisions runs at import (the assembly's
HttpServiceConfig is visible there): Authorization on an authed
namespace is rejected outright, a default_headers name match (compared
case-insensitively) fails import naming both keys, and an ApiKey
header_name match fails naming the credential header. Per the task's
decision note: the import-time surface does see the adapter's config,
so the loud point stays at import rather than first-call warn-once.

Tests: Authorization+Bearer, X-Tenant/x-tenant case-insensitive
default_headers, x-api-key/X-API-Key, and the no-collision clean path.
2026-08-31 01:46:45 +00:00
glm-5.3-flash 207bca4f23 fix(gateway): block services/schema spec disclosure via the op path (review-002 PRJ-16)
Internal/ACL-restricted op specs were readable through
POST /call {"operation":"services/schema","input":{"name":...}}
(the MCP call/batch tools identically): the outer-name pre-checks pass
(services/schema is External) and alkcall's services_schema_handler
projects any registered spec with no visibility/ACL check of its own
(alkcall CF-004 is the complete fix there).

- GatewayDispatch.invoke/invoke_streaming now apply the GET /schema
  route's is-internal + access-control checks to the meta-op's inner
  name input before dispatch (404 Internal / FORBIDDEN ACL), one
  interception point covering /call, /batch, /subscribe and the MCP
  call/batch tools; /publish cannot reach the Query-typed meta-op
- the visibility+ACL check is one shared fn (schema_disclosure_denial)
  used by the HTTP /schema route, the dispatch guard, and the MCP
  schema tool, so transports cannot drift
- when CF-004 lands, this guard remains as defense-in-depth (ADR-071)

Tests: dispatch-spine guard unit tests; /call 404 + 401/403 matrix,
/batch NOT_FOUND entry, /subscribe error event; MCP call/batch tools
via services/schema with an Internal inner name (mcp feature).

Verify: cargo test (405), --all-features (523), clippy default and
--all-features --all-targets -D warnings, fmt --check — all pass.
2026-08-31 01:32:59 +00:00
glm-5.3-flash 6490d15573 test(client): PEM parse-failure arms + config() TLS-reload assertion (CLI-03, COV-11, FWD-12)
- corrupt CA bundle section -> CaBundleParse naming the path (the
  pure-non-PEM variant parses as zero sections and is accepted by
  reqwest, so the arm needs structurally broken PEM)
- garbage client identity -> ClientCertParse naming the cert path,
  with the error asserting key material is never echoed
- config() reflects a reloaded CA bundle on the TLS path (FWD-12)
2026-08-31 01:15:09 +00:00
glm-5.3-flash fea7565613 fix(adapters): bound import/call error messages from spec-derived lists (OAI-17)
Import errors echoed unbounded spec-derived strings: a 100k-path
servers-override list produced a multi-megabyte SchemaParse message.
forward::bounded_join caps list echoes at 8 items / 128 chars per item
with a ', … (+N more)' suffix, and is applied at the servers/callbacks
/security location lists, the unbound-placeholder and unbound-remnant
lists in build_registration/forward, and the call-time declared-keys
echo. resolve_ref caps interpolated $ref strings at 128 chars.

Tests: 100k-path servers fixture asserts message < 4 KiB (linear,
completes fast); bounded_join unit test pins count+width truncation
with unchanged small-case output.
2026-08-31 01:12:43 +00:00
glm-5.3-flash ac6b4b6c9a fix(gateway): unify INVALID_INPUT to 422 on hand-rolled paths + sink deadline (GW-16, GW-17)
GW-16: empty body / malformed first line / missing header fields /
per-line cap / batch over-cap rejections now route through
call_error_to_http_response_with_identity, mapping INVALID_INPUT to
422 — same status as mid-stream chunk errors. One error class, one
status.

GW-17: invoke_sink wraps the registry sink invoke in the same 30 s
tokio::time::timeout the Once-op invoke uses; a hung sink handler
surfaces as a TIMEOUT (504, retryable) error envelope instead of
holding the HTTP connection forever. The sink wrapper bounds the
whole dispatch (chunk pacing included), matching http-server.md's
deadline contract.

Docs: http-server.md error table documents the 422 triggers and the
sink deadline; http-adapters.md batch cap status corrected.

to_openapi: gateway spec version 1.3.0 -> 1.4.0 (ADR-045 minor):
/publish framing faults and /batch cap reject documented at 422 (the
400 slots moved with the runtime); /publish 400 slot removed; 504
description covers the sink dispatch.

Verification: scripts/verify.sh OK (397 tests); cargo test
--all-features OK (513 tests); clippy --all-features --all-targets -D
warnings OK; cargo fmt --check OK.
2026-08-31 01:03:29 +00:00
glm-5.3-flash f5e75d318a fix(adapters): loud OAI-14 blocks on import — callbacks, security, oneOf requestBody, discriminator/xml warns
The OAI-06 loudness matrix had holes: callbacks, security requirement
blocks, top-level oneOf requestBodies, and schema-level
discriminator/xml keywords all vanished silently at import.

- callbacks + security: OpenAPISpec::validate_import_loud_features
  (new, run from FromOpenAPI::import) rejects with locations and
  remediation. Scoped to the service-import path, not the shared
  from_value parse — the published gateway doc round-trips through
  from_value inside to_openapi and legitimately declares security
  markers for its external clients.
- top-level oneOf requestBody (no content map): refused in
  parse_operation as an unresolvable/content-less body (OAI-15 arm,
  message names OAI-14 oneOf).
- discriminator/xml inside consumed schemas: per-operation
  tracing::warn listing the ignored keys (JSON-forwarding-only stance).

Tests: op-level callbacks, doc-level + op-level security, oneOf
requestBody each fail import naming the feature and location.
2026-08-31 01:03:18 +00:00
glm-5.3-flash 0837e942ab test(client,adapters): retry policy + streaming terminal arms + credential/header-parameter arms (CLI-03, COV-10, COV-12)
- new tests/retry_policy_wire.rs: POST+500 = 1 upstream hit (method
  gate), GET 500/503/200 = 3 hits (retry-to-success), budget exhaustion
  stops retries under a 50-attempt cap (wall-time + hit-count bounds)
- forward_stream terminal arms on the wire: oversized SSE line ->
  one INTERNAL terminal envelope; mid-stream transport abort (staged
  via a notify gate so the abort is genuinely mid-stream) -> terminal
  envelope after the delivered frame; pending event EOF-flush; dead
  port through both forward() and forward_stream()
- Once-path decode arms: malformed application/json 200 -> INTERNAL
  decode envelope; application/octet-stream 200 -> byte-array envelope
- COV-12 credential arms: ApiKey and Basic malformed values fail
  loudly without echoing secret material; declared header-param
  invalid name/value rejections
- new tests/client_config_reload.rs: config() reflects a reloaded
  config (FWD-12 atomicity half)
2026-08-31 01:00:29 +00:00
glm-5.3-flash 3f0b59b7d5 fix(adapters): warn on $ref sibling keys, document 3.0-only reading (OAI-10)
$ref siblings are ignored under OpenAPI 3.0 semantics but would apply
under 3.1 — a 3.1-authored constraint beside a $ref previously
vanished silently, overstating /schema. warn_ref_siblings now fires at
each $ref consumption point (operation parameters, requestBody,
path-item parameters) naming the location and dropped keys, and the
module doc records the version stance: no openapi 3.1 gate, 3.0
reading with the warn as the visibility mechanism.
2026-08-31 00:50:17 +00:00
glm-5.3-flash 688b7e91b2 fix(adapters): self-ref'd or content-less requestBody fails import, not silent body-less op (OAI-15)
A requestBody $ref that could not resolve (or resolved to a shape
without a content map) previously turned into a content-less Operation
registered silently — every call would INVALID_INPUT on the gateway
body key. parse_operation now treats an unresolvable request-body ref
and a resolved-but-content-less body the same unfaithful-modeling way
as an unresolvable parameter ref: the operation fails import via the
OAI-04 'unresolvable $ref' arm, whose message now names the
requestBody case (OAI-15) alongside the parameter one.

Tests: self-ref requestBody, missing-component requestBody ref, and
content-less component requestBody each fail import loudly.
2026-08-31 00:46:14 +00:00
glm-5.3-flash 02a59dfa22 test(adapters): array-of-$ref resolution + style:simple/explode:true rejection (COV-12 review-002)
Array branch of resolve_refs_bounded expands items $refs (memo hit on
repeat resolution); simple+explode=true import fails naming OAI-06.
2026-08-31 00:44:18 +00:00
glm-5.3-flash 847e5ca586 test(adapters): wire tests for same-host/cross-host/hop-cap redirect policy (CLI-02)
- same-host 302 followed with the credential header arriving at the
  followed hop (FWD-03's scrub is cross-host only)
- cross-host 302 surfaced as HTTP_302 with the attacker endpoint
  receiving zero requests (load-bearing FWD-03 property, pinned)
- redirect loop trips the hop cap -> loud INTERNAL transport error
2026-08-31 00:43:39 +00:00
glm-5.3-flash e6077fdb6b test(server): static-site decoy resolves directory requests to index.html (COV-12 review-002) 2026-08-31 00:43:32 +00:00
glm-5.3-flash 5f872f4084 test(gateway): /publish first-line invalid JSON returns 400 INVALID_INPUT (COV-12 review-002) 2026-08-31 00:43:03 +00:00
glm-5.3-flash d0e9d4e608 test(mcp): from_mcp wraps non-object tool arguments as {"value": …} (COV-12 review-002)
Wire-level round trip through the real rmcp server: a scalar input
reaches the remote tool as {"value": <input>}, matching the
value_to_json_object wrap.
2026-08-31 00:41:37 +00:00
glm-5.3-flash 8a98a52624 test(mcp): schema/batch missing-argument INVALID_INPUT arms (COV-12 review-002)
schema tool with None arguments and batch tool without a calls array
both return INVALID_INPUT naming the required field, without dispatching.
2026-08-31 00:40:23 +00:00
glm-5.3-flash 9a5b4e7936 fix(adapters): hoist path-template validation into shared forward core, run at from_openapi import (JS-02)
Import-time path-template checks now run on both adapters:
- from_jsonschema: validate_path_template moves to forward.rs (same
  checks, same messages, shared implementation)
- from_openapi: build_registration calls it before building each op, so
  a template like /x{open fails import with 'unterminated placeholder'
  instead of a per-call INTERNAL on first invoke
- new test: unterminated_path_template_fails_import_not_first_call
2026-08-31 00:39:54 +00:00
glm-5.3-flash 21e19abb60 test(mcp): rmcp-protocol call_tool round-trip per gateway tool + unknown (COV-11b review-002)
peer.call_tool through the real rmcp streamable-HTTP transport for
schema/call/batch/unknown — the production ServerHandler::call_tool
routing shell, previously only exercised via the invoke_tool bypass
for search.

Verification: cargo test --features 'mcp test-support' --test full_surface
2026-08-31 00:38:38 +00:00
glm-5.3-flash d9971e9fad fix(adapters): zip-iterate reject_collisions, drop asserts (JS-03)
assert_eq! in library code was a panic-family residue in the import
collision check (review 002 JS-03). The three vectors are built in
lockstep by import(); zipping them preserves the pairwise walk without
the panic path.
2026-08-31 00:33:59 +00:00
glm-5.3-flash a7f10ed04c chore(tasks): mark fwd16, fwd13, cli01 completed 2026-08-31 00:23:46 +00:00
glm-5.3-flash d5446c2780 Merge branch 'wt/review-002-cli01-retry-after-budget' 2026-08-31 00:18:57 +00:00
glm-5.3-flash 8c55747029 Merge branch 'wt/review-002-fwd13-dot-segments' 2026-08-31 00:17:57 +00:00
glm-5.3-flash cfbefe6d04 fix(client): budget-aware Retry-After sleep + earliest-deadline re-arm clamp (CLI-01)
A logical request's Retry-After waits are now bounded by
max_total_retry_duration, and a retry storm can no longer re-arm a
full ceiling per attempt:

- BudgetClock anchored per logical request by RetryGateMiddleware
  (into the request Extensions, shared across retry attempts) and
  read by the inner RetryAfterMiddleware every attempt; the monotonic
  anchor projects the hard stop through wall-clock steps.
- maybe_sleep_for truncates the sleep to the remaining budget;
  a spent budget skips the sleep entirely.
- record() keeps the EARLIEST deadline per URL (retry storms cannot
  extend the first-seen deadline); a refresh that cannot make it
  under the budget hard stop drops the entry so the next attempt
  starts immediately instead of parking.
- Middleware without a budget anchor (budget = 0) keeps the prior
  semantics; the shared client now wires
  HttpClientConfig.max_total_retry_duration into the Retry-After
  middleware.
- Wire tests (tests/retry_after_budget.rs): always-429 responder with
  a 300 s Retry-After is bounded by budget + one attempt; separate
  logical requests still honor the recorded throttle window.
- FWD-12 atomic reload pairing and the FWD-15 stream-client split
  untouched (stack built in build_client_with_pems for both).
2026-08-31 00:16:19 +00:00
glm-5.3-flash 58bdba35cf fix(adapters): post-set_path normalization invariant check (FWD-13)
- assemble_request_url now verifies after Url::set_path that the decoded
  URL path segments are byte-identical to base_dir + decoded rendered
  segments; a mismatch (any future normalizer rewrite in the url crate)
  fails loudly with INTERNAL instead of silently re-routing an
  authenticated request
- property-style corpus test over dot/percent/binary values: accepted
  values must survive byte-identical as one literal segment with no lone
  dot segments; failures may only be INVALID_INPUT

Verification: scripts/verify.sh (383 passed) and --all-features (499
passed), clippy -D warnings, fmt --check all pass.
2026-08-31 00:08:12 +00:00
glm-5.3-flash d81e7ef319 fix(adapters): reject lone dot/dot-dot path values normalized away by Url::set_path (FWD-13)
- value_to_path_segment now rejects scalar values whose decoded form is
  exactly '.', '..', '%2e', '%2E', '%2e%2e', or '%2E%2E' (case-insensitive)
  with INVALID_INPUT: url 2.5.8 Url::set_path silently normalizes lone
  dot segments away, so such values would route to a different upstream
  endpoint than the template describes, with namespace credentials attached
- rejection is exact-match on the full decoded segment: dotted values
  like v1.2.3, .hidden-file, ..hidden, ... still render
- error names the failure mode but never echoes the raw value
- empirically pins the set_path normalization behavior in a test
  (tenants/../resources -> /resources, /files/.. -> /, %2E%2E -> normalized)

Verification: scripts/verify.sh (382 passed) and --all-features (498
passed), clippy -D warnings, fmt --check all pass.
2026-08-30 23:57:00 +00:00
glm-5.3-flash a427dc194d fix(adapters): refuse unauthenticated send when capability is absent (FWD-16)
The FWD-08 remediation made every malformed credential fail loudly, but
an authed operation whose registry capability was entirely absent fell
through the build_request match: the request was sent with no credential
and no diagnostic, producing corrupted upstream 401s at call time.

- build_request now returns an INTERNAL error naming the missing
  capability keys (api_key:{ns} / http_token:{ns}) when an auth scheme
  is declared and Capabilities::get is empty; the request is not sent
- auth_scheme: None behavior unchanged (unauthenticated ops stay
  unauthenticated); error message carries key names only, no secret
- module doc: loud-missing matrix now covers malformed name/value AND
  absent capability
- tests: unit loud-error across all three schemes, unchanged-arm pin,
  wire test asserting the upstream receives zero requests (mirrors the
  FWD-08 test family)
- from_openapi/from_jsonschema no_env_vars tests updated: they pinned
  the old silent fall-through; still assert no env material echoes

Verification: cargo test (380 passed), cargo test --all-features
(496 passed), clippy --all-targets -D warnings (default + all-features),
cargo fmt --check — all via scripts/verify.sh
2026-08-30 23:55:42 +00:00
glm-5.3-flash 9819c24b4f chore(tasks): mark yaml-normalization, mcp-batch-cap, cov13, projection-truthfulness completed 2026-08-30 23:36:41 +00:00
glm-5.3-flash 1a5ae8748a Merge branch 'wt/review-002-projection-truthfulness' 2026-08-30 23:08:42 +00:00
glm-5.3-flash 8611920fdb Merge branch 'wt/review-002-cov13-dead-code'
# Conflicts:
#	src/gateway/dispatch.rs
2026-08-30 23:07:39 +00:00
glm-5.3-flash ccef4a7ca9 Merge branch 'wt/review-002-mcp-batch-cap' 2026-08-30 23:06:23 +00:00
glm-5.3-flash e98e495137 test(full_surface): refresh /openapi.json version pin to 1.3.0 2026-08-30 23:04:25 +00:00
glm-5.3-flash 60362c4d0f docs(architecture): align http-adapters.md to_openapi section with the 1.3.0 projection
- /call response map: 415 slot, identity-split 401 oneOf, PRJ-17 merge
  note, PRJ-19 shape-rejection note
- /batch: no-500 statement (PRJ-21) and the BatchError in-band error
  component (PRJ-16b)
- adapter.rs openapi.json test pin refreshed to 1.3.0
2026-08-30 22:56:53 +00:00
glm-5.3-flash 29d98b8c22 fix(adapters): projection doc truthfulness in to_openapi (PRJ-16b/17/18/19/20/21/23, info.version 1.3.0)
- PRJ-16b: BatchResultEntry.error now refs a defined BatchError
  component (oneOf over the six protocol-code envelopes plus a generic
  BatchOperationError arm carrying the operation-declared code enum);
  the dangling #/components/schemas/CallError ref is gone
- PRJ-17: operation-declared errors at protocol statuses with
  HTTP_-prefixed codes merge into the shared protocol response's oneOf
  (per-code CallError_<code> components) instead of clobbering it —
  the runtime genuinely emits both; non-protocol statuses overwrite
  as before
- PRJ-18: /publish 400 dropped the INVALID_OPERATION_TYPE claim
  (runtime reports that at 401 without a token, error.rs); the 401
  entry is the true one and already documented
- PRJ-19: 415 (missing/non-JSON Content-Type) and plain-text 422
  (shape-rejection) extractor slots documented, extending the
  plain-text extractor rejection family; /subscribe gains 415/422,
  /call gains 415 with the shape-rejection noted on 422, /search and
  /schema gain the slots too
- PRJ-20: /call 401 now carries the identity-split oneOf (FORBIDDEN +
  INVALID_OPERATION_TYPE), matching error.rs's 401-without-identity
  mapping for both
- PRJ-21: /batch's unreachable 500 removed (all dispatch failures are
  in-band entries; routes.rs has no 500 path)
- PRJ-23: the OAS-invalid x-operation-error-statuses pseudo-schema key
  inside components.schemas removed (nothing consumed it; any openapiv3
  registry rejects it as an invalid schema name)
- info.version 1.3.0 per ADR-045 (doc-contract corrections, wire
  contract unchanged)

Verification: cargo test (to_openapi suite 42/42 green, incl. the
populated-registry openapiv3 parse and deterministic golden checks)
2026-08-30 22:55:38 +00:00
glm-5.3-flash 4f644a4c2c feat(mcp): cap MCP batch tool at MAX_BATCH_OPERATIONS (PRJ-22)
- enforce the same 100-operation cap the HTTP /batch endpoint enforces;
  over-cap \x60calls\x60 reject with a structured INVALID_INPUT (retryable:
  false, matching CallError::invalid_input) before any dispatch
- hoist MAX_BATCH_OPERATIONS to gateway/mod.rs and reuse it in routes,
  to_openapi (removing a pre-existing duplicate literal), and to_mcp
- state the limit in the batch tool description and add maxItems to the
  input schema (doc previously advertised no limit)
- GatewayDispatch gains a per-instance invoke_count spy accessor so the
  over-cap test proves zero dispatches (process-global counters raced
  under the parallel test runner)
- tests: over-cap -> INVALID_INPUT + invoke_count()==0; at-cap -> 100
  results + invoke_count()==100

verification: scripts/verify.sh (352 passed) and scripts/verify.sh
--all-features (468 passed); cargo clippy --all-targets -D warnings and
cargo fmt --check clean
2026-08-30 22:51:06 +00:00
glm-5.3-flash 08584b229d refactor(gateway): delete dead accessors and FromRef impls (review 002 COV-13)
Coverage-confirmed dead code (every binary, zero hits):

- server/state.rs: drop FromRef<RouterState> impls for
  Arc<OperationRegistry> and Arc<dyn IdentityProvider> — no route
  extracts these types; the auth middleware receives the provider
  directly via from_fn_with_state
- gateway/dispatch.rs: drop identity_provider() and resolve_bearer()
  accessors; resolve_bearer's doc promised an auth hook the middleware
  never calls (spec/code drift). Wire-or-delete resolved to delete:
  bearer resolution lives in the middleware (SRV-11 single-resolve
  ordering), the dispatch spine only needs the per-call
  Option<Identity>. GatewayDispatch::new consequently takes the
  registry alone (GatewayState loses its unused identity_provider
  passthrough; dispatch.rs/to_mcp.rs tests simplified)
- websocket/upgrade.rs: drop FromRef<SessionState> for
  Arc<OperationRegistry> — no router carries SessionState as its state
  type; the inverse FromRef<Arc<OperationRegistry>> for SessionState
  (custom upgrade routes, integration tests) remains

Verification: ./scripts/verify.sh (352 passed), ./scripts/verify.sh
--all-features (466 passed), clippy -D warnings, fmt --check.
2026-08-30 22:50:15 +00:00
glm-5.3-flash 67ae7cd9fe docs(adr-051): YAML/JSON parity contract for the OAI-12 normalization pipeline
§5 records the post-OAI-12 from_yaml contract: duplicate keys rejected
loudly on YAML (with the empirical correction that serde_json 1.0.151's
Value path last-wins rather than errors — the YAML side is the stricter
one), non-finite floats rejected with JSON-pointer context, merge keys
applied via apply_merge (shallow, referencing keys win; the one
deliberate YAML 1.2 deviation), scalar-key stringification matching the
core schema, and the no-new-bounds note (the walk stays inside
yaml_serde's parse-time limits).

Verification: cargo doc --no-deps clean; module-doc cross-check in
openapi_spec.rs matches this contract.
2026-08-30 22:38:04 +00:00
glm-5.3-flash 1728b4881e feat(adapters): YAML input normalization — duplicates, .inf/.nan, merge keys, non-string keys (OAI-12)
from_yaml previously went straight yaml_serde::from_str::<serde_json::Value>
with zero post-parse normalization. Three verified corruptions flowed
through unimpeded:

- duplicate mapping keys silently last-won (serde_json's visit_map
  insert semantics — verified against 1.0.151: the JSON path last-wins
  too, so the YAML path is now deliberately the stricter one);
- .inf/-.inf/.nan scalars silently became Value::Null because
  serde_json::Number::from_f64(non-finite) is None;
- YAML 1.1 merge keys (<<: *anchor) survived as literal '<<' properties;
- non-string mapping keys (null keys, collection keys) either stringified
  through YAML's debug rendering or panicked the conversion.

The new from_yaml pipeline: explicit yaml_serde::Value parse (native
loud duplicate-key rejection with line/column) -> apply_merge() (merge
keys applied, shallow per yaml_serde semantics; scalar/invalid merge
values fail loudly) -> one structural normalization pass into
serde_json::Value that rejects non-finite floats and non-string keys
with the offending JSON pointer, and stringifies scalar keys exactly as
the YAML 1.2 core schema renders them (200: -> "200", matching the
JSON path's {"200": ...}).

The walk stays inside yaml_serde's own parse-time bounds (recursion
limit 128, alias jump limit, RepetitionLimitExceeded); no new budgets
and no resolver changes. 14 seam tests added at the from_yaml boundary.

Verification: cargo test 366 passed / 0 failed; clippy
--all-targets -D warnings clean; fmt --check clean (scripts/verify.sh).
2026-08-30 22:37:34 +00:00