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

74 lines
3.2 KiB
Markdown

---
id: review-001-gateway-publish-semantics
name: /publish + /batch gateway fixes (GW-01, GW-06, GW-08..GW-11, HY-13)
status: pending
depends_on: []
scope: narrow
risk: medium
impact: component
level: implementation
tags: [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 `chunk``INVALID_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.