# Allowlist for the serde policy wall (see driver_adoption.rs,
# `provider_streaming_modules_never_raw_parse_the_wire`).
#
# Format: `path suffix | line snippet | one-line justification`.
# Every entry must justify why the raw parse is NOT wire-frame triage:
# frame decoding belongs to the wire.rs classifiers and the adapter driver.
# Stale entries (matching no shipped line) fail the guard — delete them
# alongside the code they covered.



# --- openai Responses SSE: classifier-adjacent helpers ---------------------
# Single-file providers: unary/non-stream parses sharing a file with streaming code.

# --- #[serde(untagged)] unions that are not frame triage --------------------

# --- openai chat wire (providers/openai/wire/chat.rs) ----------------------
# `chat.rs` IS OpenAI's classify layer, so guard 1 lets it name the triage
# variants — but naming them is not licence to raw-parse, so the serde wall
# scans it like any other file and every parse below is argued here. Four of
# the five are `classify`'s own work; the fifth reads a sub-payload of a
# frame the classifier already returned as `Known`. The sixth is the wire's
# observation projection, argued with its two siblings in the section below.
providers/openai/wire/chat.rs | serde_json::from_str::<Value>(&data) | `classify` reads a frame the shared classifier called `Corrupt` (a duplicate `object` or `choices` key) once more, only to find the in-band error envelope, which outranks every other reading of a frame; anything else stays `Corrupt`.
# A chat chunk's `delta.reasoning_details` is deliberately typed
# `Vec<serde_json::Value>`: the entries are a gateway extension, so an entry
# shape this build does not model must not fail the chunk that carries it.

# --- the observation projections, folded into their decoders ---------------
# `Decoder::project` is a read-only side channel onto the observation trace.
# It runs on a payload the driver has ALREADY framed and already classified,
# it returns nothing, and its whole failure mode is emitting no fact: the
# frame's fate was decided by `classify` and is identical whether the
# projection parsed the payload or not. That is why each of these parses is
# a bare `let Ok(..) else { return }` rather than a `Corrupt` verdict — a
# projection that could reject a frame would be a second classifier. These
# three parses (four sites) moved here when the `observation.rs` modules
# were folded into the decoders that were their only callers.
providers/anthropic/streaming.rs | serde_json::from_slice::<Value>(payload) | `MessagesDecoder::project` reads the stop reason, model, message id, usage and error envelope off a payload the driver already framed and classified, through the decoder's own lenient usage reader; a failed parse emits no observation and leaves the frame's verdict exactly as `classify` set it.
providers/cohere/streaming.rs | serde_json::from_slice::<Value>(payload) | `ChatDecoder::project` reads the finish reason, reply id and usage off a payload the driver already framed and classified; a failed parse emits no observation and leaves the frame's verdict exactly as `classify` set it.
providers/gemini/streaming.rs | let Ok(reply) = serde_json::from_slice::<Value>(payload) else { | `GenerateContentDecoder::project` reads the already framed and classified payload as `Value` for the observation projection; every field is read leniently and a payload that is not JSON emits nothing, so the frame keeps the fate `classify` gave it.
providers/ollama/streaming.rs | let Ok(record) = serde_json::from_slice::<Value>(payload) else { | `ChatDecoder::project` reads the already framed and classified record as `Value` for the observation projection; every field is read leniently and a payload that is not JSON emits nothing, so the frame keeps the fate `classify` gave it.
providers/openai/wire/chat.rs | serde_json::from_slice::<Value>(payload) | `ChatDecoder::project` reads the verdict, model, response id, usage and error envelope off an already-framed, already-classified payload — `[DONE]` is not JSON and simply projects nothing — so a failed decode emits no observation and triages no frame.

# --- openai modality wire: whole replies that have no frames ----------------
providers/openai/wire/modality.rs | #[serde(untagged)] | `ImagesReplyImage` is the reply-side union of the two forms two dialects put under the `images` key (Hyperbolic `{"image": "<base64>"}`, Venice the bare base64); the image route answers with one whole reply and has no frames, so no inbound frame policy is decided here.
providers/openai/wire/modality.rs | let reply = match serde_json::from_slice::<SpeechReply>(&event) | `SpeechDecoder::interpret` unwraps the JSON envelope Hyperbolic alone wraps its audio in; `classify` already returned the bytes as `Known` because the speech reply is audio rather than JSON, and a decode failure is pushed as a ProviderError rather than skipped.

# --- openai responses websocket: envelope dispatch, not frame triage -------
# The websocket carries the SAME Responses events the SSE path carries, and
# `parse_server_event` routes every one of them through `classify_responses_frame`.
# What it decodes itself is only the two envelopes the websocket adds around
# that shared set: the transport-level `error` event and the terminal
# `response.done`. Neither exists on the SSE wire, so neither has a classifier
# to route to.
providers/openai/responses_api/websocket.rs | serde_json::from_str::<EventType>(payload) | Reads only the `type` tag to decide whether the frame is one of the two websocket-only envelopes; every other frame is handed to classify_responses_frame untouched, so this dispatches rather than triages.
providers/openai/responses_api/websocket.rs | "error" => serde_json::from_str(payload) | Decodes the websocket-only `error` envelope, which has no SSE counterpart and therefore no classifier; a decode failure is returned as a ProviderError, never skipped.
providers/openai/responses_api/websocket.rs | "response.done" => serde_json::from_str(payload) | Decodes the websocket-only `response.done` envelope, which has no SSE counterpart and therefore no classifier; a decode failure is returned as a ProviderError, never skipped.

# --- the driver: capturing a reply body is not decoding a frame -----------
# `CompletionResponse::raw` is the provider's own reply, which is what every
# `raw_completion` caller used to read off a provider-typed response. The
# driver captures it from the bytes it already has, as an opaque `Value`, and
# decides nothing: a body that is not JSON becomes `Value::Null` and the
# reply's frames are decoded by the wire's decoder either way.

# --- the wire model: classification that is the classify layer's own work --
providers/gemini/model_listing.rs | let page: Value = serde_json::from_slice(body) | The catalog page's decode on the listing operation, whose reply is one JSON document and has no frames to triage; every failure is returned as `parse_error` with the provider, path and body preview, never skipped.
rig-bedrock/src/embedding.rs | .and_then(|body| serde_json::from_str(&body).map_err(ProviderError::Json)); | InvokeModel returns the model's whole JSON body as an opaque blob; the transport decodes that one document into the typed reply before any frame exists, and a malformed body is the frame's error, never skipped.
rig-bedrock/src/image.rs | serde_json::from_str::<TextToImageResponse>(&body) | The unary InvokeModel reply is one opaque JSON blob with no frames to triage; its typed decode fails the call with a ProviderError, never skipped.
rig-bedrock/src/streaming.rs | let input = serde_json::from_str::<Value>(input) | Reads a tool use's accumulated input text, already delivered by its classified events, as the object its provider item holds; text that is not an object keeps a client call's item off and gives a hosted use `{}`, nothing is skipped.

# --- vertexai: SDK reply restated as the REST chunk ------------------------

# --- item-shaped decoders: typed views of classified frames ----------------
# Each decoder keeps a classified frame as JSON so the provider's item survives
# verbatim, then reads one field of it through its typed form.

# --- cohere chat: one decoder for two APIs ---------------------------------
