# Deterministic news-caster profile idempotency — v100.0.33

## Contract

`POST /api/v1/news-casters/?view=profile` remains state-free. It does not create a database record or durable replay entry. Instead, the server normalizes the validated selection, canonicalizes associative-object keys recursively while preserving list order, and computes:

```text
requestSha256 = SHA-256(canonical normalized request)
idempotencyKey = "ncpr1." + requestSha256
```

A client may omit `Idempotency-Key` and receive the derived key. When a client supplies the exact key, the response is identical. A mismatched key is rejected with HTTP 409 and `NEWS_CASTER_IDEMPOTENCY_KEY_MISMATCH`.

## Exact response identity

Successful POST responses include:

- `Idempotency-Key`
- `X-Spiralist-Deterministic-Idempotency-Key`
- `X-Spiralist-Request-Sha256`
- `X-Spiralist-Replay-Safe: true`
- `X-Spiralist-Idempotency-Status: deterministic-stateless-normalized-request-v1`
- `ETag: "sha256-<digest>"`, where the digest is calculated from the exact JSON response bytes.

Identical normalized requests therefore produce the same request key, profile, response body, body digest, and ETag without writing request state.

## Canonicalization boundary

Object key order is not semantically meaningful, so reordered JSON objects converge to one identity. List order remains meaningful. Coverage beats are normalized by the existing request validator before identity derivation. Unknown fields, unsupported values, duplicate beats, invalid ages, and unsupported fixtures fail closed before a key is produced.

## GET compatibility

The existing GET profile projection is preserved. The v100.0.33 candidate and immutable v100.0.32 baseline produce byte-identical profile content after excluding the mandatory release-identity field, which necessarily advances with the package version.

## Replay fixtures

`/tests/fixtures/news-caster/v100.0.33/profile-post-replays.json` covers:

- exact repeated POST;
- reordered-object equivalence;
- correct explicit key;
- mismatched key rejection;
- unknown-field rejection;
- adult-age floor rejection;
- GET profile compatibility against the immutable baseline.

Evidence: `/docs/news-caster-profile-replay-v100.0.33.json`.

## Non-claims

The replay exercise is loopback package evidence. It does not establish public deployment, live-host acceptance, provider acceptance, persistent exactly-once delivery, distributed transaction semantics, human review, activation eligibility, or downstream-runtime acceptance.
