# Upstream OpenAPI specs

Two complementary upstream sources, normalized to YAML for diff-friendly storage.
Everything in this directory is **auto-generated by** `poe refresh-upstream-spec`
(script: [`scripts/pull_upstream_specs.py`](../../scripts/pull_upstream_specs.py)). **Do
not hand-edit.** Refresh by re-running the task.

## Files

### [`live-gateway.yaml`](./live-gateway.yaml)

The canonical OpenAPI spec served by the live API gateway at
`https://api.katanamrp.com/v1/openapi.json`. Public endpoint, no auth. This is what the
gateway *actually validates requests against*.

- **Use it for**: request DTOs, paths, filter-param enums, component schemas. This is
  the authoritative source for *what the API accepts*.
- **It does not have**: response shapes. Every `200` response is just
  `{description: "Return value of FooController..."}` with no schema or example.

This is what `poe audit-spec` compares the local spec against.

### [`readme-portal.yaml`](./readme-portal.yaml)

The OpenAPI spec embedded in any reference page on `https://developer.katanamrp.com/` —
lifted out of `<script id="ssr-props">` at `document.api.schema`. The pull script
derives the page slug at runtime via the README.io Algolia search index, so it doesn't
depend on any specific endpoint slug being stable.

- **Use it for**: response examples. Every endpoint's
  `responses.<status>.content.<ctype>.example` carries a real JSON payload curated by
  Katana's docs team. This is the primary source `poe validate-response-examples`
  validates our local response schemas against.
- **It is slightly behind** the live gateway on path coverage. Treat request-side info
  here as advisory only — `live-gateway.yaml` wins for request DTOs.

## Refresh

```bash
poe refresh-upstream-spec
```

Idempotent. Both fetches run in parallel via `asyncio.gather`. Output is written through
`yaml.safe_dump(..., sort_keys=True)` so diffs are stable across runs.

## Workflow

The full spec-maintenance loop, in order. Step 1 writes to `docs/upstream-specs/`
(idempotent — re-running with no upstream changes is a no-op); steps 2–4 are read-only.
None of steps 1–4 touch `docs/katana-openapi.yaml` or any generated client code, so
they're safe to run any time.

```
1. uv run poe refresh-upstream-spec      # pull live gateway + README.io portal
2. uv run poe audit-spec                 # request-side drift vs. live-gateway.yaml
3. uv run poe validate-response-examples # response-side drift vs. readme-portal.yaml
4. uv run poe validate-examples          # local internal consistency
5. edit docs/katana-openapi.yaml         # address findings
6. uv run poe regenerate-client && uv run poe generate-pydantic
7. commit the spec edit + regen output together (per CLAUDE.md)
```

What each step catches:

- **`audit-spec`** — paths/operations the local spec is missing or has extra; required
  fields, type/enum drift on request DTOs. The live gateway is canonical here.
- **`validate-response-examples`** — drift in our local response schemas, surfaced by
  validating Katana's own README.io response examples against them. The live gateway has
  no response shapes, so this is the only structured signal we have for response
  accuracy.
- **`validate-examples`** — every inline `example:` block in `docs/katana-openapi.yaml`
  validated against its own schema. Catches typos and examples left behind when fields
  change.

Findings split into three buckets: real local-spec drift (fix in the spec),
documentation artefact (Katana's docs are wrong, verify against live API and just record
it), or needs verification (flag for a human to check against the live API). Past audit
reports under `docs/archive/2026-*/audit-*.md` are good reference.

The spec-auditor agent (`.claude/agents/spec-auditor.md`) drives steps 1–5 and produces
a triaged report; invoke it via the `Agent` tool when you want a fresh audit.

## Why YAML?

JSON would also work, but:

- YAML is quote-free / comma-free → easier to read in PR review.
- `sort_keys=True` gives stable diffs across upstream re-orderings.
- The same formatter is used for both files, so they diff cleanly against each other
  (e.g. *which paths are in `live-gateway` but not in `readme-portal`?*) and against our
  local spec at [`../katana-openapi.yaml`](../katana-openapi.yaml).

## Why two specs and not one?

Katana publishes the OpenAPI spec in two places, each with different content:

- The **live gateway** ships the spec the runtime validator uses — request DTOs and
  filter enums — but no response schemas or examples (the gateway just doesn't carry
  response info in its OpenAPI doc).
- The **README.io developer portal** ships a spec with inline response examples curated
  by humans for the docs site. It's slightly behind on request-side coverage but it's
  the only structured place response examples exist.

Each spec is the source of truth for a different part of our local spec. Pulling both
keeps our audit tooling honest in both directions.

## History

The legacy `docs/katana-api-comprehensive/` directory contained scraped markdown for
every endpoint plus an embedded OpenAPI JSON. The scrape broke when README.io migrated
to a JS-rendered SPA, and the markdown parsing was always brittle. This directory
replaces that legacy archive with structured, refreshable, normalized YAML.
