## Log-level policy

Per ADR-0012, this component's `error!` sites are categorized as:

- **(e) outside-contract** (lib.rs L748, L774):
  - L748 = accept-loop error in `run_axum_server`. Calls `runtime.metrics().increment_errors(route_id, "e:http:accept")` BEFORE the `error!`. The metric is the operator signal; `error!` provides loud log visibility.
  - L774 = server task exited unexpectedly in `monitor_axum_task`. Calls `runtime.metrics().increment_errors(route_id, "e:http:server-task-exited")` BEFORE the `error!`. Same pattern.
  Both sites keep `error!` with `// log-policy: outside-contract`.

- **(c) system-broken** (lib.rs L1108): `Body::Stream` already consumed before HTTP reply — programming-contract violation in `dispatch_handler`. Keeps `error!` with `// log-policy: system-broken`. No metric call (operator alert via error! is the signal).

- **(a) handler-owned** (lib.rs L1210): pipeline error processing HTTP request → 500 response in `dispatch_handler`. Route ErrorHandler owns the ERROR. Downgraded to `warn!` with `// log-policy: handler-owned`. No metric call.

Reviewer: r_glm5.1 verifies these classifications against source at Phase C review time.

## Contract Surface

Per ADR-0024 and spec §3.4. Documents the accepted and rejected names/values for the HTTP consumer reply finaliser. Future bug reports check here first: if behaviour is in this surface, it's a feature request; if not, it's a bug.

### Accepted — reply status code

- `CamelHttpResponseCode` header on the Exchange (type `u16` as JSON number, or string-parseable-as-u16 in range `100..1000`). Drives the HTTP response status.
- If header absent: `200 OK` (normal completion) or `200 OK` (Stop — post-ADR-0024, same code path).
- The `200`-on-empty-Stop replaces the legacy `204` default. Users wanting `204` set `CamelHttpResponseCode=204` explicitly.

### Accepted — reply body

- `Body::Empty` → empty body.
- `Body::Bytes(b)` → raw bytes.
- `Body::Text(s)` → UTF-8 bytes, `Content-Type: text/plain; charset=utf-8` unless overridden.
- `Body::Xml(s)` → UTF-8 bytes, `Content-Type: application/xml`.
- `Body::Json(v)` → JSON-serialised bytes, `Content-Type: application/json`.
- `Body::Stream(s)` → streamed; `Content-Type` from `s.metadata.content_type`. Body MUST NOT be already consumed (system-broken `error!` if it is).

### Accepted — reply headers

- All Exchange headers EXCEPT:
  - Headers starting with `Camel` (Camel-internal namespace).
  - Hop-by-hop / request-side headers: `content-length`, `content-type` (use the explicit Content-Type derivation above), `transfer-encoding`, `connection`, `cache-control`, `date`, `pragma`, `trailer`, `upgrade`, `via`, `warning`, `host`, `user-agent`, `accept`, `accept-encoding`, `accept-language`, `accept-charset`, `authorization`, `proxy-authorization`, `cookie`, `expect`, `from`, `if-match`, `if-modified-since`, `if-none-match`, `if-range`, `if-unmodified-since`, `max-forwards`, `proxy-connection`, `range`, `referer`, `te`.
- User-supplied `Content-Type` header on the Exchange overrides the inferred content type.

### Rejected

- `Body::Stream` already consumed before reply → `500 Internal Server Error` + empty body (system-broken `error!` at lib.rs:1109).
- Pipeline returns `Err(CamelError::Unauthenticated(msg))` → `401 Unauthorized` + `WWW-Authenticate: Bearer` + body "Unauthorized".
- Pipeline returns `Err(CamelError::Unauthorized(msg))` → `403 Forbidden` + body "Forbidden".
- Pipeline returns `Err(_)` (any other error) → `500 Internal Server Error` + body "Internal Server Error".

### Silent behaviour forbidden

- There is NO `Err(CamelError::Stopped)` special-case arm. Stop arrives as `Ok(ex)` (ADR-0024) and is handled by the same reply-finaliser path as normal completion. A future regression that re-introduces a Stop special-case is a bug.

### Stop-specific contract

- `stop: true` after `set_body` + `set_header("CamelHttpResponseCode", "409")` produces HTTP `409` + the body — identical to a route that reaches the end of the pipeline with the same Exchange state.
- `stop: true` with no body and no status header produces HTTP `200` + empty body (NOT `204` — the legacy `204` default was Bug B adjacent behaviour, removed in Phase 3).
