Files
alkhttp/tasks/gateway/review-001-gateway-publish-semantics.md
T
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

3.2 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
review-001-gateway-publish-semantics /publish + /batch gateway fixes (GW-01, GW-06, GW-08..GW-11, HY-13) pending
narrow medium component implementation
gateway
review-001

Description

Review 001 findings on src/gateway/routes.rs's /publish and /batch routes:

  • GW-01 (major): /publish (routes.rs:257-276) feeds parsed NDJSON straight to invoke_sink; publish_schema validation lives only in alkcall's wire Dispatcher — so a Pub op registered with publish_schema receives arbitrary attacker-controlled JSON over HTTP while the same op over the call protocol aborts invalid chunks. Handlers written against the validated-wire guarantee get a transport-dependent invariant. Fix: validate in the route (or move validation into the shared invoke_sink spine so both transports enforce it — prefer the spine if alkcall's surface allows, else the route).
  • GW-10: a first line missing chunk silently publishes Value::Null (routes.rs:213) — indistinguishable from intent since null is a legitimate payload. Reject with INVALID_INPUT (the route already rejects a missing operation this way).
  • GW-06: /publish buffers the whole NDJSON body (2 MiB-capped) before dispatch, contradicting ADR-068 step 4. Either stream the body (axum Body → framed stream) — the real fix — or amend ADR-068 to document the buffered 2 MiB semantics. Decide, then implement or amend.
  • GW-08: no cap on batch operation count; the 2 MiB body is the only bound and the 30 s deadline is unenforced (GW-05). Cap batch size (constant, e.g. 100).
  • GW-09: internal-op batch entries emit request_id: null while dispatched entries carry a UUID — one response body, two envelope shapes. Generate request ids for internal-op entries.
  • GW-11: /publish runs four registry lookups + ACL checks that invoke_sink then repeats — drop the redundant pre-checks (mirror /call//batch, which deliberately rely on the registry).
  • HY-13: the vacuous test at routes.rs:1572-1583 (publish_body_is_fully_consumed_before_dispatch_not_required) cites a socket-level test that doesn't exist — wire the early-disconnect test or delete the stub.

Acceptance Criteria

  • /publish test with a publish_schema-registered Pub op rejects an invalid chunk (review's gate for this unit)
  • First line without chunkINVALID_INPUT, not a null publish (test)
  • GW-06 decision landed: true streaming or ADR-068 amended; consistent tests + docs
  • Batch size capped (test); mixed-shape batch envelopes fixed
  • Redundant /publish pre-checks removed (dispatch still enforces)
  • cargo test and cargo clippy --all-targets -- -D warnings pass

References

  • docs/reviews/001-initial-implementation-review.md (Part C, GW-01, GW-06, GW-08..GW-11; HY-13)
  • docs/architecture/decisions/068-gateway-publish-endpoint.md
  • docs/architecture/decisions/023-operation-error-schemas.md

Notes

Agent fills during implementation. If the GW-01 fix goes into alkcall's invoke_sink spine, coordinate the alkcall change (small, additive) and note it in the summary.

Summary

Filled on completion.