# 050 — Whole-story content contract

## One canonical pack

Both AI and authored demos produce the same pack. The engine accepts only a validated normalized pack. Keep the validator pure and stdlib-based; JSON mode alone does not validate facts, length, or required fields.

Implemented external JSON shape:

```json
{
  "schema_version": 1,
  "status": "ready",
  "identity": {
    "title": "The Matrix",
    "year": 1999,
    "media_type": "movie",
    "season_number": null,
    "episode_number": null,
    "episode_title": ""
  },
  "difficulty": "easy",
  "scenes": [
    {
      "chapter": 1,
      "setup": "Neo receives an unexpected instruction on his computer.",
      "question": "What is he told to follow?",
      "options": {
        "A": "The white rabbit",
        "B": "A black car",
        "C": "A red telephone",
        "D": "A stranger in a coat"
      },
      "correct": "A",
      "reveal": "He is told to follow the white rabbit. A visitor's tattoo leads him to the club."
    }
  ],
  "ending": "A concise closing paragraph describing the selected work's actual ending."
}
```

The example is an English schema illustration with **one scene**, not a valid complete Easy pack or the final Czech fixture. Production text is Czech, matching the current game's prompt convention; interface strings are separately localized in cs/sk/en.

For insufficient knowledge use only:

```json
{"schema_version": 1, "status": "unavailable", "reason": "A short factual explanation of the knowledge limitation."}
```

Do not include a partial `ready` pack. An unavailable response is not permission to improvise a generic story.

## Identity and scope

The requested immutable subject is authoritative. Returned identity is an echo for mismatch detection, not a replacement subject or a reason to overwrite art/metadata. The implementation requires the exact supplied title after case/whitespace normalization, alongside exact known year/scope. The prompt explicitly asks the model to copy identity without translation. Do not infer translated aliases without evidence or silently accept another work of the same year. Literal brackets in a metadata title such as `[REC]` are valid; narrative markup remains prohibited.

Use known title/year/media/episode details in the request. Null means a field was not supplied, not that a model should guess it. If the existing subject has a TMDb ID, retain it in request/session context; do not require the model to know that numeric ID.

For an ongoing work, `ending` describes its known story endpoint without inventing closure. For a specific episode, it closes that episode only.

## Field limits and writing targets

These are the current production validator limits. Option length was tightened from the initial 40 to 30 after actual Kodi clipping evidence; these limits are independent of Millionaire's contract.

| Field | Hard maximum | Target |
|---|---:|---|
| `setup` | 220 characters | 1–2 short sentences, normally 120–180 characters |
| `question` | 140 characters | One clear plot question |
| Each option | 30 characters | Short meaningful actions/events; calibrated against the existing 32px answer tiles |
| `reveal` | 240 characters | 1–2 sentences resolving this question and connecting the event |
| `ending` | 220 characters | 1–2 concise final sentences |
| Unavailable `reason` | 220 characters | Plain limitation, no invented plot |

Keep these limits in one code location and interpolate them into prompts. If measured answer tiles require a tighter option limit, update the same constants, prompt, fixtures and tests together. Never truncate an answer until it changes meaning or hides the difference from a distractor. Narration autoscroll is the fallback for normal wrapping, not a reason to accept unlimited output.

## Structural validation

Reject before play when any of these fail:

- Root object, supported version, known status, complete `ready` fields with correct types.
- Requested difficulty and exactly 10/15/20 scenes according to the configured mapping.
- Exactly five ordered chapter groups 1..5, each with `question_count / 5` scenes. No skipping, backtracking, missing beginning/ending, or extra chapter.
- Nonempty setup, question, reveal, ending and option texts inside limits.
- Exactly keys A/B/C/D, four distinct options after whitespace/case normalization, exactly one declared letter in A–D.
- Valid identity/scope fields consistent with the request; no boolean masquerading as a numeric season/year.
- No duplicate normalized question or duplicate full scene. Repeated canonical events/answer meanings are additionally inspected in fixture review, not falsely proven by string validation.
- Plain narrative text: no executable Kodi markup, unexpected control characters, literal A–D correctness markers embedded in setup, or arbitrary tags. Escape/sanitize UI control syntax consistently; do not display hidden answer fields by accident.

Do not silently substitute empty fields, pad missing options, take the first of several answers, or default `correct` to A. Do not repair a failed pack by dropping questions; it would destroy full-story coverage.

Simple text equality can detect an exact answer leak in setup/question, but semantic leaks and plot correctness need content review; avoid rejecting legitimate overlap such as a character name common to every answer.

## Normalization and randomness

Validate the raw shape and declared correct letters first. Normalize options to the same stable internal four-option order and a correct index once at the content boundary, then validate the normalized result before publication. Freeze the normalized pack for the attempt. Keep the original correct-option meaning intact when redistributing letters; normalization is never a substitute for rejecting an invalid source field.

AI packs shuffle normalized option indexes locally after validation. They preserve the exact correct option meaning and use that mapping consistently for reveal/TTS/UI. Millionaire's placement algorithm remains unchanged. Do not require a model to balance letters perfectly; authored demo letters remain fixed for repeatable evidence.

Scenes are ordered by their array position. Do not sort by in-universe dates, chapter labels, or guessed timestamps. The setup of scene N+1 must account for the canonical reveal of N even when the player answered incorrectly.

## Error semantics

- Invalid JSON or structurally invalid pack -> Error with localized Retry / Back; zero scenes playable, three lives untouched.
- `unavailable` -> same user-visible recovery flow, showing a concise safe reason.
- Provider timeout/failure -> provider error mapped to the existing error-card style; do not expose credentials or raw transport payloads.
- Retry is an explicit new preparation attempt, not an automatic repair loop or fallback provider.
- No automatic paid schema-repair request. Record the type of validation failure for debugging without retaining a reusable generated story.

No confidence score, self-rating, JSON validator, or second question can prove factual correctness. The initial implementation has knowledge-based generation, not independent verification.
