# Exercise 2: Diagnose and Fix a Drifted Contract

## Scenario

`StackShelf`'s `GET /books/{id}` endpoint has been live for a year. A consumer opens a support ticket:

> "Your docs say `GET /books/{id}` always returns a `genre` field as a string, but I'm seeing `genre: null` in production for some books, and my client crashes trying to call `.toUpperCase()` on it. Also your docs show `available_copies` as always present, but I just got a response missing that field entirely for book id 4021."

You pull the actual current OpenAPI schema on file (below) and the actual current handler code (also below, simplified).

**Documented schema (current, on file):**
```yaml
Book:
  type: object
  required: [id, title, genre, available_copies]
  properties:
    id: { type: integer }
    title: { type: string }
    genre: { type: string }
    available_copies: { type: integer }
```

**Actual handler (simplified, current):**
```ts
function serializeBook(book: BookRow) {
  return {
    id: book.id,
    title: book.title,
    genre: book.genre ?? null, // legacy rows imported before genre tracking existed have NULL here
    // available_copies is computed from a join; omitted entirely if the join finds no inventory row
    ...(book.availableCopies !== undefined ? { available_copies: book.availableCopies } : {}),
  };
}
```

## Deliverables

Produce all of the following, in this file, below this line:

1. **A one-paragraph root-cause explanation** of exactly why each of the two consumer-reported symptoms happened, tracing each back to the specific line of `serializeBook` that produced it.
2. **A corrected OpenAPI schema** for `Book` that honestly reflects what the handler can actually return today (i.e., document reality) — including which fields may legitimately be `null` (via `nullable: true`) versus which are truly always present, and update `required` accordingly.
3. **A short remediation plan** (3-5 bullet points) covering both possible directions: (a) changing the *code* to match the original promise (always populate `genre` and `available_copies`), versus (b) changing the *docs* to match the code (as in your answer to #2) — state which one you'd recommend for this case and why, referencing theory section 5's point about contract tests.
4. **A contract-test sketch** (pseudocode or real TypeScript, a few lines) that would have caught this drift automatically instead of via a support ticket, per theory section 5.

## Constraints

- Your corrected schema in #2 must be internally consistent: no field can be both in `required` and marked `nullable: true` in a way that contradicts your own explanation in #1.
- Do not simply mark every field `nullable`/optional as a shortcut — justify each change against the actual handler behavior shown above.
