docs(adr-066): record FWD-17/18/19 forwarding contract decisions
SSE payload contract (non-JSON frames carry {data, event}; JSON frames
surface as themselves), the placeholder routing rule (placeholder keys
never double-emit as query; structural path values are INVALID_INPUT),
and the literal-percent trade-off (% in values always encoded; % in
assembly-supplied template text survives — the assembly owns the
upstream-semantics choice, per the ADR-066 trust boundary).
This commit is contained in:
@@ -149,6 +149,56 @@ error projection and the success sweep (SSE detection +
|
|||||||
output-schema selection), where the precedence order is: concrete 2XX
|
output-schema selection), where the precedence order is: concrete 2XX
|
||||||
statuses, then `2XX`, then `default`.
|
statuses, then `2XX`, then `default`.
|
||||||
|
|
||||||
|
### Forwarding contract decisions (review 002 FWD-17/18/19)
|
||||||
|
|
||||||
|
The shared forwarding core (`src/adapters/forward.rs`) used by both
|
||||||
|
`from_openapi` and `from_jsonschema` pins three contracts. Each is a
|
||||||
|
decision the code half-implied; the module doc carries the full normative
|
||||||
|
text, this section records the reasoning at the assembly-trust boundary
|
||||||
|
that ADR-066 establishes: an assembly's spec/template/base-URL inputs
|
||||||
|
are trusted configuration, while peer call-time input never is.
|
||||||
|
|
||||||
|
**FWD-17 — SSE payload contract (decided: carry raw payload + event
|
||||||
|
name for non-JSON frames).** A subscription frame's `data:` payload
|
||||||
|
that is itself valid JSON surfaces as the decoded value (`123` stays a
|
||||||
|
number, `"123"` stays a string). Any other payload surfaces as the
|
||||||
|
`{"data": <raw>, "event": <name|null>}` wrapper: a legitimately
|
||||||
|
non-JSON stream stays field-addressable and an upstream's named-event
|
||||||
|
conventions (`event: error`) are visible instead of indistinguishable
|
||||||
|
from data. The `event` value is the frame's `event:` field under
|
||||||
|
WHATWG last-wins semantics — `null` when absent, never the implicit
|
||||||
|
`message` default, so "named" and "default" stay distinguishable. JSON
|
||||||
|
frames surface as themselves even under a named event; consequence:
|
||||||
|
the event name is only visible on non-JSON frames. Chosen over the
|
||||||
|
alternative (document the old JSON-or-string contract) because the old
|
||||||
|
behavior silently erased payload shape and made an upstream's error
|
||||||
|
convention unobservable — a fidelity loss on the same axis ADR-023
|
||||||
|
already rejects for status mapping.
|
||||||
|
|
||||||
|
**FWD-18 — placeholder routing rule (decided: fix + structural-value
|
||||||
|
error).** A key matching a path-template placeholder is consumed by the
|
||||||
|
path and never also emits as a query parameter, whatever its value's
|
||||||
|
shape (the renderer's placeholder check precedes query routing — fixed
|
||||||
|
behavior, pinned by test). A placeholder renders exactly one literal
|
||||||
|
path segment, so a structural value (object/array) under a placeholder
|
||||||
|
key fails with `INVALID_INPUT` rather than splicing minified JSON into
|
||||||
|
the path. Scalar values (string/number/boolean/null) render
|
||||||
|
percent-encoded as before.
|
||||||
|
|
||||||
|
**FWD-19 — literal `%` in template text (decided: documented
|
||||||
|
trade-off, not rejection).** A `%` inside a *value* is always encoded
|
||||||
|
(`%` → `%25`), so a value can never inject or fake percent escapes. A
|
||||||
|
`%` in *template or base text* survives verbatim: an assembly writing
|
||||||
|
`%2F` into a template is presumed to intend a pre-encoded segment for
|
||||||
|
upstreams that route `%2F` differently from `/`. Inputs are
|
||||||
|
assembly-supplied (this ADR's trust boundary), so the strict shape
|
||||||
|
(call-time rejection of raw `%` in template text) is rejected — it
|
||||||
|
would break legitimate pre-encoded templates without adding safety:
|
||||||
|
the surviving `%2F` still renders as a single literal segment, cannot
|
||||||
|
change the origin, and cannot be forged from peer input. The trade-off
|
||||||
|
(upstream-dependent routing of bare percent escapes) is the
|
||||||
|
assembly's choice, not this crate's.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
**Positive**:
|
**Positive**:
|
||||||
|
|||||||
Reference in New Issue
Block a user