v0.37.1 — assert the wire headers are sent, not merely declared
Two new shared-suite groups. Ports may see new failures — that is the point.
Both close the same gap in two more places: a rule asserted where it is declared and never where it applies.
TestErrorHeader — the error flag must be sent
A failed RPC answers HTTP 200. The error rides the body as an EXCEPTION batch, because the call reached the method and the method raised — so the status line says nothing, and X-VGI-RPC-Error: true is the entire signal that distinguishes a failure from a result.
The suite required that header to appear in Access-Control-Expose-Headers and never checked that any response carried it. A port could expose a header it never sends and pass clean. Exposing what you do not emit is not the smaller bug; both leave a client reading a failure as success.
Two assertions, both directions:
test_error_response_sets_the_flag |
a raising method answers 200 with X-VGI-RPC-Error: true |
test_success_response_does_not_set_the_flag |
a successful call does not |
The second matters as much as the first: a flag set on every response carries no information, which is the same outage as never setting it.
Ungated — every HTTP port runs these.
TestRequestId — the correlation id must agree with the log
X-Request-ID on the response and request_id in the access log must name the same request. An id present on the response but absent from the log, or different between them, is worse than having none — it looks like a working trail right up to the moment someone tries to follow it.
test_response_carries_a_request_id |
an id is emitted, generated when none was sent |
test_inbound_request_id_is_echoed |
a caller's id is propagated, not replaced |
test_generated_ids_differ_between_requests |
a minted id is not a constant |
test_request_id_matches_the_access_log |
header and record name the same request |
docs/access-log-spec.md §4.4 makes propagation a SHOULD, so the header cases skip for a port that emits nothing rather than failing it. Agreement is not optional: a port that emits both must make them equal.
The correlation case needs to read what the server logged, so it is gated on a new optional fixture:
conformance_http_access_log -> (port, path)
a worker started with --access-log PATH. Omit it and only that one case skips.
The sweep
These were found by looking for every place the suite checks an advertisement without checking the corresponding behaviour — the shape behind the last three bugs. No further instances exist. Everything else already asserts behaviour:
| capability | behaviour assertion |
|---|---|
VGI-Max-Request-Bytes |
413 on an oversized inline body |
VGI-Max-Response-Bytes |
strict-cap group raises on overshoot |
VGI-Supported-Encodings |
test_empty_advertisement_means_never_compressed |
VGI-Sticky-Default-TTL |
drives a real expiry from the advertised value |
VGI-Sticky-Enabled / echo headers |
round-trip asserted |
VGI-Proxy-Proof-Required |
enforcement asserted |
VGI-Auth-Reason / -Proxy-Required |
asserted on real 401 responses |
VGI-Upload-URL-Support / -Max-Upload-Bytes |
upload flow exercised |
For porters
If TestErrorHeader fails, check that your error path sets X-VGI-RPC-Error: true on the 200 it returns — and that your success path does not. If TestRequestId::test_request_id_matches_the_access_log fails, your header and your log are naming different requests, which no amount of grepping will reveal in production.
Both groups were verified by sabotage before shipping: suppressing the flag, setting it unconditionally, minting an id instead of echoing one, and stamping a different id on the response each fail the case that covers them.
Upgrading: no action for library users — this is conformance tooling. No public API changes; no wire-protocol changes; protocol_hash unchanged.