Commit Graph
11 Commits
Author SHA1 Message Date
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 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 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 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 91483a74b4 docs: missing_docs sweep — 0 warnings + deny gate + publish-prep decisions (HY-02, HY-04, HY-11)
- document every public-API item across 18 files (openapi_spec model,
  HttpAuthScheme/HttpServiceConfig, HttpClientBuildError + SharedHttpClient
  accessors, RetryAfterMiddleware, GatewayDispatch, gateway error
  mapping, CallRequest/SchemaQuery/SubscribeStream, HttpAdapter +
  ALPNs + builders, decoy/healthz/state, WsSessions/WsPumps,
  from_openapi/from_jsonschema/from_mcp/from_wss/to_mcp, lib.rs module
  docs)
- enforcement: #![deny(missing_docs)] at crate root — stronger than CI
  rustdocflags (every build incl. cfg(test), where rustdoc misses the
  test-support module docs)
- HY-10 (opportunistic): all 8 docs.rs/alkhttp placeholder ADR links +
  the one relative ../docs link converted to plain text; the 10
  pre-existing private/redundant intra-doc-link warnings fixed —
  RUSTDOCFLAGS="-D warnings" cargo doc is fully clean
- HY-11 decision: docs/ + tasks/ excluded from the published package
  (contributor-facing design/process material; ADR references degrade
  to plain text uniformly). cargo publish --dry-run: 38 files, ~889 KiB,
  zero docs/ or tasks/ entries
- HY-04 decision: keep + document — frame_channel0_chunk's unwrap is
  on serializing the acyclic EventEnvelope (unreachable failure);
  # Panics on it and the adjacent WsClient senders state the contract

Verified: cargo test (299 + 5 TLS), --all-features (370 + suites),
--no-default-features (299), clippy --all-targets -D warnings
(default + all-features), fmt --check, cargo doc -D warnings clean,
cargo publish --dry-run --allow-dirty clean.

Tasks: review-001-missing-docs-sweep (final pending task; 42/42)
2026-08-30 08:25:18 +00:00
glm-5.3-flash 1572a9d2d0 fix(gateway): fail-closed publish_schema compile + compile-once cache (GW-01 follow-up)
- new gateway::schema_cache — PublishSchemaCache: compile the op's
  publish_schema once per registration (value-keyed invalidation for
  hot reload), cache compile failures (logged once at error level,
  never retried per request)
- /publish compile failure is now fail-closed: the chunk stream
  terminates with INTERNAL (500), the error text stays in the log
  (no schema internals on the wire) — the per-request warn-and-skip
  unvalidated ingest path is removed
- schema resolution is lazy (first chunk poll, after invoke_sink's
  404/403/422 pre-checks — GW-11 order preserved) and keyed by schema
  value, so re-registration/hot reload is picked up (test)
- NdjsonChunkStream: first Err item is terminal (done + stream end),
  mirroring the wire pump's send(Err) + break — Ok chunks can never
  follow an error on the HTTP path either (found by spy-handler test)

Verified: cargo test (308), cargo test --all-features, clippy
--all-targets -D warnings (default + all-features), fmt --check.

Tasks: review-001-publish-schema-validation-robust
2026-08-30 07:02:27 +00:00
glm-5.3-flash 25975ac2a8 fix(websocket): configurable WS read idle timeout (WS-01) 2026-08-29 13:45:54 +00:00
glm-5.3-flash fa73684ebe fix(websocket): configurable WS session cap (WS-09) 2026-08-29 13:37:08 +00:00
glm-5.3-flash 4747a12c02 fix(websocket): retain WsPumps handle on the server path (WS-08) 2026-08-29 13:31:33 +00:00
glm-5.3-flash 314472012d fix(server): hyper knobs, decoy fidelity, cache + builder fixes (SRV-04..SRV-10)
- SRV-04: TokioTimer on h1+h2 builder, header_read_timeout 10s,
  h1 keep-alive on, h2 keep-alive 30s/10s; concurrency boundary documented
- SRV-05: with_decoy rebuild keeps extra routes (clone, not take)
- SRV-07: method_not_allowed_fallback serves the nginx-shaped 405
- SRV-08: UTF-8 percent-decoding, literal '+', tokio::fs syscalls
- SRV-09: /openapi.json cached at construction, generic 500 body,
  to_openapi returns Result (expect removed)
- SRV-10: ChannelsPolicy extension injection point on the WS upgrade;
  single token resolution via route ordering (WS layer before the
  router-wide auth route_layer)

Verification: cargo test 265 passed; --all-features server::/to_openapi::
green; clippy + fmt clean on touched files (remaining tree noise is a
parallel agent's in-flight from_mcp/from_wss/forward work)
2026-08-29 10:47:44 +00:00
glm-5.3-flash d070e548ad feat: server foundation (phase 1 core) — state, auth, healthz/decoy, gateway dispatch, HttpAdapter
Tasks completed: server-core-types, server-auth, server-healthz-decoy,
gateway-dispatch, server-adapter (5 of 17).

- src/server/state.rs: DecoyConfig + RouterState (alkcall type paths,
  6-endpoint reserved-path docs)
- src/server/auth.rs: bearer middleware + ResolvedIdentity extractor
  (10 tests: missing/malformed/basic/failed-resolution matrix)
- src/server/healthz.rs + decoy.rs: raw healthz; nginx-style 404,
  static site (path-traversal guarded), redirect decoys
- src/gateway/dispatch.rs: GatewayDispatch invoke/invoke_streaming
  (internal:false, forwarded_for:None, bounded deadline) +
  src/gateway/error.rs: CallError→HTTP status mapping (HTTP_<status>
  passthrough, retryable→Retry-After)
- src/server/adapter.rs: HttpAdapter ProtocolHandler — accept_bi →
  BiStream → TokioIo → hyper auto builder (h2 CONNECT enabled);
  integration tests over DuplexStream (request/response cycle, healthz,
  decoy 404)

Verified: cargo test (46 lib tests), clippy -D warnings, fmt,
test --all-features.
2026-08-28 07:35:02 +00:00