28 Commits
Author SHA1 Message Date
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 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 a7f10ed04c chore(tasks): mark fwd16, fwd13, cli01 completed 2026-08-31 00:23:46 +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 8261fefd8f chore(tasks): mark js01, con18-wss, fwd17-19, con18b completed 2026-08-30 22:12:40 +00:00
glm-5.3-flash 5244dc46e2 chore(scripts): coordinator verify gate + mark gw15/oai13 completed 2026-08-30 20:45:35 +00:00
glm-5.3-flash ef6eab020d chore(tasks): summaries + oai11 completion for wave-1 merges 2026-08-30 20:08:44 +00:00
glm-5.3-flash a71592e42c chore(tasks): mark review-002 wave-1 tasks completed (fwd15, ws13, con17, srv11/12) 2026-08-30 20:08:28 +00:00
glm-5.3-flash e2c255d40c docs(tasks): decompose review-002 into 24 tasks (23 implementation + 1 bracketed follow-up)
Decomposition of docs/reviews/002-post-remediation-review.md per its
5-unit remediation plan:

- Unit 1 (security-critical): gw15-publish-body-cap,
  prj16-schema-via-call (CF-004 filed alkcall-side), fwd13-dot-segments,
  fwd16-missing-capability, oai11-ref-memoization
- Unit 2 (timeout/terminality): ws13-idle-progress,
  fwd15-stream-timeout, cli01-retry-after-budget, con17-mcp-pagination,
  con18-wss-sweep-exit
- Unit 3 (projection/docs): projection-truthfulness, mcp-batch-cap,
  gw16-status-drift
- Unit 4 (spec-import): yaml-normalization, oai13-path-item-wildcards,
  import-loudness-cluster, js01-placeholder-check,
  fwd17-19-contract-decisions
- Unit 5 (WS polish + tests): con18b-ws-polish,
  client-policy-wire-tests, cov-deployment-knobs, cov13-dead-code,
  srv11-srv12-router-ordering
- review-002-bracketed-followup: tentatively planned post-bulk pass
  (stale-check, OQA-18 enforcement decision, CON-08/09 close() lever,
  cross-crate re-checks) — deliberately not serialized against the
  bulk

Also: review-002 numbering repair (CON-14 was double-booked; MCP
pagination now CON-14, from_wss monitor renumbered CON-18, missing
CON-14 section added).

taskgraph: 66 valid, no cycles; 24 pending (all review-002);
gen-1/gen-2 parallel waves identified; workflow-cost hotspots are
prj16 (12.8) and ws13 (11.1), both carrying the reviewed slicing
guidance in their Notes.
2026-08-30 10:50:34 +00:00
glm-5.3-flash 7ce1ca6fbd feat(adapters): loud unsupported-OpenAPI-feature handling (OAI-06)
- build_error_schemas: default/wildcard response keys dropped with a
  warn instead of emitting a dead HTTP_0 ErrorDefinition — /search
  never advertises a code that can't match (the runtime mapper already
  synthesizes HTTP_<actual> for unmapped statuses)
- check_parameter_style: non-default style/explode parameter forms
  (spaceDelimited, pipeDelimited, deepObject, form+explode:false,
  simple+explode:true) fail import with a feature-naming SchemaParse;
  wire-equivalent defaults (form, simple) import unchanged — no more
  silent "[1,2]" array mis-serialization
- servers overrides rejected at import at all three levels (document,
  path, operation) — the adapter pins one base_url at assembly time
- trace-only paths: skip is now logged (warn naming path + methods),
  documented-as-inert instead of silent
- detect_op_type + build_output_schema sweep 2XX/default keys for
  text/event-stream — a default-declared SSE stream classifies as Sub
  instead of returning one giant text body

Tests: 11 new (error-drop, style rejections + default accept, servers
3-level rejections + baseline, trace skip, SSE default/2XX detection).
Verified: cargo test (299), --all-features (370 + suites), clippy
--all-targets -D warnings (default + all-features), fmt --check.

Tasks: review-001-openapi-loud-degradation
2026-08-30 08:10:40 +00:00
glm-5.3-flash 5d6945cd4b docs(tasks): post-remediation sweep — 4 follow-up tasks
Found in the sweep of completed review-001 remediation:
- /publish schema validation fails open on compile error + recompiles
  per request (remediation-introduced, routes.rs:254-268)
- OAI-06 loud-degradation unblocked and still open (HTTP_0 marker)
- HY-06 ExponentialBackoff in public API + COV-02 mTLS success path
  both unblocked post client-config rework
- HY-02/04/11 publish-prep docs gate (104 missing-docs warnings
  re-measured)

Also flagged, not tasked here: WS-12 (alkcall demux 4 GiB discard
alloc) was never actually filed in alkcall's consumer-findings-ledger —
only CF-001 is there. File it when next touching alkcall.

taskgraph: validate clean (42), no cycles
2026-08-30 06:03:29 +00:00
glm-5.3-flash 6fa2d4c036 fix(adapters): MCP tool-gateway fidelity (PRJ-07..10, PRJ-13)
- search honors the optional query substring filter (PRJ-07)
- search excludes both Sub andPub ops per ADR-041/ADR-068; dead match
  arms removed (PRJ-08)
- batch returns {"results": [...]} with {isError, output|error} items;
  tool description states the shape (PRJ-09)
- structuredContent is always an object: non-object outputs wrapped as
  {"result": <output>} (PRJ-10)
- argument errors are CallError values (retryable always present);
  non-string operation no longer reports 'missing required field'
  (PRJ-13)

Verified: cargo test --all-features, cargo test, clippy -D warnings,
fmt --check
2026-08-29 12:53:03 +00:00
glm-5.3-flash a9d17405a7 fix(adapters): input-schema enforcement + header/cookie params (OAI-02, OAI-03, OAI-07, OAI-09)
- build_request takes the op's input_schema and rejects undeclared
  input keys (INVALID_INPUT) before any outbound request is built;
  explicit `additionalProperties: true` opts into catch-all input;
  non-object inputs rejected (OAI-02)
- in: header parameters are stamped `wire: header` in the generated
  input schema and sent as upstream request headers, not query params;
  in: cookie fails import with a clear error (OAI-03)
- a spec parameter named `body` is rejected at import unconditionally
  (OAI-07)
- FromJsonSchema::new returns Result and validates method/path template/
  base_url at construction; registered visibility forced to Internal
  like from_openapi; module doc corrected (OAI-09)

Verified: cargo test, cargo test --all-features, clippy (both feature
sets, -D warnings), cargo fmt --check
2026-08-29 12:48:13 +00:00
glm-5.3-flash 7b83161171 docs(tasks): review-001 openapi-projection-fidelity remediation complete 2026-08-29 12:47:34 +00:00
glm-5.3-flash 8700ed0fea fix(adapters): consumer adapter hygiene (CON-01, CON-03..CON-13)
- CON-01: from_mcp discovery follows tools/list pagination
  (rmcp list_all_tools); three-page paginating-server test
- CON-03: from_wss refuses ws:// with a Bearer token unless
  FromWss::allow_plaintext() is called explicitly (tests: refusal,
  opt-in, token-less passthrough)
- CON-04: audio variant of content_block_union_schema requires
  ["type","data","mimeType"]; jsonschema-validated audio block
- CON-05/07: import-time credential documented on both adapters;
  dead per-call capability read removed
- CON-06: 401 classification typed-first (downcast to rmcp
  StreamableHttpError<reqwest::Error>; AuthRequired/InsufficientScope/
  Client with status 401); a :40101 URL no longer misclassifies (tested)
- CON-11: transport tools/call failures declare MCP_TRANSPORT_ERROR;
  rmcp JSON-RPC errors preserve code (MCP_JRPC_<code>) and data
- CON-12: tool names validated at import (/, whitespace, empty →
  SchemaParse); unit + integration tests
- CON-13: tokens held as alkcall Secret<String> (zeroize, redacted Debug)
- CON-08/09: no close handles; explicit-limitation notes in from_mcp
  module docs, from_wss module docs, and ADR-070
- CON-10: full_surface [[test]] required-features = ["mcp","test-support"];
  cargo test --features mcp now compiles and passes

Verified: cargo test; cargo test --features mcp; cargo test --all-features;
cargo clippy (--all-features) --all-targets -- -D warnings; cargo fmt --check
2026-08-29 10:54:33 +00:00
glm-5.3-flash 1dc1d5af4f docs(tasks): review-001 response-decode fidelity (FWD-07/08/10/12) complete 2026-08-29 10:46:18 +00:00
glm-5.3-flash 5df91cda25 fix(adapters): resolve parameter/requestBody refs + reject import collisions (OAI-04, OAI-05)
- index components/parameters + requestBodies in OpenAPISpec; resolve
  bare $ref parameter and requestBody entries through the cycle-guarded
  resolver; unresolvable refs abort spec parse loudly
- reject path placeholders with no matching input-schema property at
  registration (no more silently percent-encoded literal placeholders)
- reject duplicate operationIds and path+method routes in one import
  batch instead of silent last-write-wins registration
- also reject a parameter named 'body' shadowed by requestBody (OAI-07
  adjacency, same code path)

Verified: cargo test (243), --all-features (322), clippy -D warnings
(default + all-features), fmt --check, doc --no-deps
2026-08-29 10:13:11 +00:00
glm-5.3-flash 713f1eba46 docs(tasks): review-001 forward-url-safety remediation complete 2026-08-29 10:09:33 +00:00
glm-5.3-flash cc34c08e4e fix(adapters): incremental byte-level SSE parser (FWD-06)
Replace per-chunk parse_sse_frames with SseParser holding raw bytes
across chunks: reassembles frames split at TCP boundaries (the review's
silently-losing case), decodes UTF-8 per complete line so multi-byte
chars split across chunks survive, caps the buffer at 1 MiB
(SSE_EVENT_BUFFER_CAP) and dispatches a pending event at EOF.

forward_stream threads the parser through its unfold state and emits a
terminal error envelope on cap overflow. Existing single-chunk SSE test
assertions preserved; added multi-chunk, split-UTF-8, EOF-dispatch, and
cap tests.

Verified: cargo test (219 pass), clippy -D warnings, fmt --check.
2026-08-29 08:35:33 +00:00
glm-5.3-flash 0a8d4d731f fix(adapters): bounded, cycle-safe $ref resolution (OAI-01, OAI-08)
resolve_refs_recursive recursed with no cycle detection and no depth
budget; a self-referential OpenAPI component stack-overflowed and
aborted the process (uncatchable, kills import()).

- add branch-scoped visited set on the JSON-pointer ref path: a ref
  re-entering its own expansion chain errors cleanly with
  AdapterError::SchemaParse naming the offending ref (OAI-01)
- add depth budget (MAX_REF_RESOLUTION_DEPTH = 64) bounding $ref hop
  chains and schema nesting height; over-deep specs error cleanly
  instead of exhausting the stack (OAI-01)
- clean loud error over depth-limited expansion: recursive schemas
  (trees, linked lists, cursor pagination) fail import rather than
  expand unboundedly
- shared refs to a common schema (diamond/repeated) still resolve —
  visited set is branch-scoped, not global
- OAI-08: replace the two guarded expects in from_value
  ("paths is object", "schemas is object") with if-let paths

Verification: cargo test 188 passed / 0 failed; clippy
--all-targets -D warnings clean; fmt --check clean
2026-08-29 07:53:01 +00:00
glm-5.3-flash 12b35e2c5f docs(tasks): decompose review 001 remediation Units 1-5 into taskgraph tasks
21 review-001 tasks across server/adapters/client/gateway/websocket/infra,
chunked from the 7-unit remediation plan in
docs/reviews/001-initial-implementation-review.md.

- Scope split by mechanism, not one-per-finding: 15 tasks in generation 1
  (parallelizable), 6 sequenced after their file-sharing precursors
- Deliberately deferred until dependent fixes land: projection/doc
  fidelity partial (Unit 6 beyond dependency hygiene), coverage backfills
  (COV-01..07 via in-task acceptance for forward.rs), and per-finding
  minors (OAI-06/07, HY-02/04/06/10/11, CON-08)
- Cross-crate WS-12 (alkcall demux 4 GiB discard alloc) noted for filing
  in alkcall, not here

taskgraph: validate clean, no cycles, 6 generations
2026-08-29 07:10:15 +00:00
glm-5.3-flash 3a906cbd6a feat(adapters): from_wss consumer adapter behind the wss feature (ADR-070)
- FromWss: dial wss:// -> split_tungstenite_to_bytes (client-side twin
  of the axum WS byte-adapter; one seam, both directions, OQ-01) ->
  Connection::from_bidi(b"alk/channels") -> alkcall ChannelClient
  (channel 0 install + dispatch loop) -> alkcall from_call importer.
  No protocol fork: specs mirror the remote, provenance FromCall.
- Drop semantics (OQ-03 v1): session drop -> monitor fails all
  in-flight pendings retryable CONNECTION_CLOSED (WsPumps::read_eof
  Notify); no 30s-deadline hang.
- Bearer token via constructor/assembly layer (ADR-014 no-env-vars).

Production fix in the WS server half (upgrade.rs): the upgrade
identity now propagates to channel 0's CallConnection (was
AuthContext::anonymous -> dispatcher saw no identity, ACL checks ran
unauthenticated; services/list filtered scoped ops for all callers).

9 in-module tests incl. full round-trip consumer<->server (both halves
of the adapter together), ACL end-to-end, drop-no-hang.

Verified: cargo test (227 lib default), --all-features (227 lib + 5
MCP + 10 WS integration), clippy -D warnings (both), fmt.
2026-08-28 14:54:09 +00:00
glm-5.3-flash 4ac337c3a5 feat(adapters): from_mcp + to_mcp behind the mcp feature (rmcp 1.8)
from_mcp (src/adapters/from_mcp/):
- tools/list discovery over streamable HTTP; per-tool
  HandlerRegistration (Mutation, Once, FromMCP leaf, Internal;
  ADR-015/022)
- structuredContent-preferred output, ContentBlock-union fallback,
  isError -> MCP_TOOL_ERROR with content blocks as details (ADR-023)
- bearer token flows via capabilities key 'mcp' (ADR-014 no-env-vars)
- 19 unit tests + tests/from_mcp_integration.rs (5 tests vs a real
  rmcp streamable-HTTP MCP server)

to_mcp (src/adapters/to_mcp.rs):
- 4 fixed gateway tools (search/schema/call/batch, ADR-041); Sub ops
  excluded from search and uncallable (MCP is request/response)
- identity survives rmcp framing: bearer_auth_middleware stashes
  Option<Identity> in http::request::Parts extensions, call_tool reads
  it back from RequestContext extensions
- StreamableHttpService nested at /mcp in HttpAdapter's router,
  bearer middleware around it (feature-gated)

Streamable HTTP only (ADR-037): rmcp default-features off, no stdio.
Default build compiles without rmcp (cargo tree: 0 hits).

Verified: cargo test (182 lib default / 218 all-features) + 5 MCP
integration + 10 WS, clippy -D warnings (both), fmt.
2026-08-28 14:14:09 +00:00
glm-5.3-flash 7be91987ca feat(adapters): from_openapi adapter (parse + forwarding handlers)
Ported FromOpenAPI on the pre-staged foundations:
- openapi_spec.rs (from_json/from_yaml/from_str JSON-first per ADR-051,
  $ref resolution) — already shared with to_openapi
- forward.rs shared forwarding core (build_request/forward/
  forward_stream/parse_sse_frames) — already shared with from_jsonschema

New in this port:
- FromOpenAPI adapter: op-id normalization (declared or
  {method}_{path}), op-type detection (GET->Query, else Mutation;
  200/201 text/event-stream -> Sub), input schema from parameters +
  requestBody 'body', error schemas as HTTP_<status> (ADR-023),
  Internal visibility + FromOpenAPI provenance (ADR-015/022),
  Query/Mutation -> Once, Sub -> Stream (Pub never produced, v1)
- 47 in-module tests: wire-level integration over real TCP
  (echo + capturing servers), bearer/api-key/basic credential
  injection from Capabilities (ADR-014 no-env-vars), SSE streaming,
  YAML + from_str + ADR-051 yes-string guards
- removed dead_code allows from openapi_spec.rs (now consumed)

Verified: cargo test (182 lib), test --all-features (182+10 WS),
clippy -D warnings (both), fmt.
2026-08-28 14:04:48 +00:00
glm-5.3-flash 42239a0af5 feat(gateway,adapters): /publish endpoint (ADR-068) + to_openapi 6-endpoint projection
gateway-publish:
- GatewayDispatch::invoke_sink (internal:false, forwarded_for:None)
- POST /publish: NDJSON body, first line {operation, chunk} (OQ-02
  resolved: first-line convention; terminal errors = plain HTTP status
  + JSON body, not NDJSON lines); 404 internal/unknown, 401/403 ACL,
  400 INVALID_OPERATION_TYPE for non-Pub
- ADR-068 + open-questions.md updated with the OQ-02 resolution

adapter-to-openapi:
- src/adapters/openapi_spec.rs: OpenAPISpec model (JSON/YAML/from_str
  JSON-first per ADR-051, $ref resolution) shared by from/to_openapi
- src/adapters/to_openapi.rs: 6-endpoint projection, info.version
  1.0.0 -> 1.1.0 (minor: /publish addition per ADR-045), /publish
  NDJSON doc with 400 oneOf (INVALID_INPUT + INVALID_OPERATION_TYPE),
  ADR-023 error fidelity (protocol statuses, HTTP_<status> passthrough,
  internal-op exclusion)
- GET /openapi.json wired into HttpAdapter's router (bearer-auth layer)

Verified: cargo test (136 lib), test --all-features (136+10 WS),
clippy -D warnings (both), fmt. Doc validates against openapiv3.
2026-08-28 13:54:49 +00:00
glm-5.3-flash ad975408e7 feat(adapters): from_jsonschema single-endpoint adapter + shared forwarding core
- src/adapters/forward.rs: shared HTTP forwarding core (extracted from
  old from_openapi) — HttpServiceConfig/HttpAuthScheme, build_request
  with capabilities-based credential injection (no env vars), forward
  + forward_stream (SSE projection), parse_sse_frames
- src/adapters/from_jsonschema.rs: FromJsonSchema OperationAdapter
  (ADR-066) — one registration per call, FromJsonSchema provenance
  (leaf, Internal default), Sub -> HandlerKind::Stream (text/event-stream)
- adapted to alkcall 0.1.1: OperationType::Sub, alkcall::client
  adapter traits

Verified: cargo test (106 lib tests), clippy -D warnings, fmt.
2026-08-28 13:43:35 +00:00
glm-5.3-flash 63dc4b6d06 docs: implementation plan (TTY precedents folded in); task decomposition (17 tasks)
Plan updated with the alknet-tty findings: the drainer pattern
(single ordered writer) makes outbound chunk-boundary parsing sound;
TestStdinSink's try_send→Full→Pending is the inbound backpressure
precedent; OQ-01(a) now cites the reference.

Task graph (taskgraph-validated, 17 tasks, 6 generations, no cycles):
- tasks/server/: core-types, auth, healthz-decoy, adapter
- tasks/gateway/: dispatch, routes, publish
- tasks/websocket/: byte-adapter (research POC), upgrade-session, overlay-ops
- tasks/adapters/: from-openapi, from-jsonschema, to-openapi, from-wss, mcp
- tasks/client/: http-host
- tasks/infra/: integration-suite (phase 4)

Critical path runs through server core → adapter → WS session →
overlay tests → integration suite. High-risk tasks are the three WS
tasks, de-risked by the ws-byte-adapter POC blocking upgrade-session.
2026-08-28 06:05:45 +00:00