> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# V2.5.2

## Safer HTTP Client Runtime

v2.5.2 hardens the HTTP client around interrupted streams, retries, telemetry, and
deterministic LLM-backed testing. The release keeps the common HTTP API focused while
making failure and replay behavior more explicit.

## Transport and Streaming Correctness

* cURL streaming now checks completed transfer results from `curl_multi_info_read()`;
  dropped connections are no longer reported as successfully completed streams.
* Redirect response headers no longer leak or merge values across response hops.
* Guzzle, Symfony, and cURL transport failures are normalized more consistently into
  connection, timeout, network, and HTTP request exceptions.
* Stream completion is reported only after natural iterator exhaustion. Interrupted
  `ArrayStream`, `IterableStream`, SSE, and decorated streams remain incomplete.
* Raw iterables passed to `HttpResponse` are rejected instead of being silently
  buffered. Use `streamingFromIterable()` or `bufferedFromIterable()` explicitly.
* SSE parsing emits a final unterminated event and enforces a bounded parser buffer.

## Safer Retries and Idempotency

* `Retry-After` delays are capped by `maxDelayMs`, preventing a server response from
  blocking a PHP worker for hours.
* Retries are limited to idempotent methods by default. `POST` and `PATCH` require an
  explicit opt-in and should be paired with `IdempotencyMiddleware` where supported.
* Generated idempotency keys remain stable across retry attempts and middleware
  ordering. Header matching is case-insensitive.
* Numeric and RFC 7231 `Retry-After` values are parsed defensively, including invalid,
  negative, fractional, and oversized values.

## Privacy-Safe Diagnostics and Telemetry

HTTP driver events and telemetry no longer attach complete request or synchronous
response bodies by default. They retain safe URLs, redacted headers, status data, and
body-size metadata instead. Optional streaming telemetry capture is bounded and
redacts captured content.

Common credentials in URLs, authorization headers, cookies, response metadata, and
known body fields are sanitized before record/replay persistence. Prompts, model
outputs, PII, and provider-specific secrets may still be present in fixtures and must
be reviewed before sharing.

## Versioned HTTP Record/Replay

The HTTP client now exposes an immutable cassette API through
`RecordReplayMiddleware::recordTo('/tmp/http-cassette')` and
`RecordReplayMiddleware::replayFrom('/tmp/http-cassette')`.

The cassette store provides:

* versioned, atomic filesystem persistence with payload bytes outside metadata JSON;
* strict ordered sessions for repeated and interleaved requests;
* hermetic replay by default, with live passthrough requiring explicit policy;
* typed missing, mismatch, exhausted, corrupt, and unsupported-version failures;
* canonical JSON request matching while preserving array order and scalar types;
* binary-safe, lazy, one-shot streamed replay without loading all chunks into a PHP
  array;
* boundary-safe credential sanitization for streamed payloads; and
* an isolated compatibility reader for existing pre-versioned one-file recordings.

Existing `RecordingMiddleware` and `ReplayMiddleware` integrations remain available as
deprecated compatibility surfaces. New integrations should use `recordTo()`,
`replayFrom()`, `recordWith()`, or `replayWith()`.

## Deterministic Example Verification

The shared `examples/boot.php` integration supports `pass`, `record`, and hermetic
`replay` modes without editing each example. Use `just examples-record
getters_and_setters`, `just examples-replay getters_and_setters`, or
`just examples-replay-all` to run selected examples or the corpus.

Replay provisions dummy provider keys only for configuration resolution and serves
recorded responses without contacting the provider. The new `examples-qa` skill
documents how to interpret hub results: `ASSERT` and `ERROR` are real failures,
live-only `FLAKY` results are tolerated but replay failures are not, and skipped
`broken`/`no-replay` examples are not evidence of a pass.

## Upgrade Notes

1. Replace direct mutable `RecordReplayMiddleware` construction and setters with
   `recordTo()` or `replayFrom()`. Use `recordWith()`/`replayWith()` for custom stores.
2. Replay now fails closed for missing interactions. If live fallback is genuinely
   required, opt into `ReplayMissPolicy::Passthrough` explicitly.
3. Existing pre-versioned recordings are read through the compatibility adapter. New
   recordings use the versioned cassette layout; refresh fixtures when convenient.
4. Review retry behavior for `POST` and `PATCH`: they are no longer retried by default.
   Opt in only when the endpoint's idempotency contract is understood.
5. Replace raw iterable response construction with
   `HttpResponse::streamingFromIterable()` or `HttpResponse::bufferedFromIterable()`.
6. Treat cassettes and diagnostic output as sensitive application data even after
   automatic credential sanitization.
