# Client-upgrade checks

> How CI proves a client on a previous SDK release can still typecheck and build against SDK HEAD, and how to refresh the frozen consumer fixtures at release.


The client-upgrade check proves that a real client pinned to the previous SDK
release can still **typecheck and build** against the SDK built from the
current commit. Every pull request packs the SDK from HEAD, installs that
exact tarball into frozen consumer fixtures, and runs `tsc --noEmit` and
`next build` in each one. Any type or build break fails CI. Deprecation
warnings are reported in the job log, never failures.

This exists because clients pin SDK builds — the MVR site installs npm
`0.1.0-beta.2` from a vendored tarball — while most of CI (for example the
example-conformance job) only ever tests consumers that move in lockstep with
the monorepo. The upgrade path is the compatibility surface clients actually
feel.

## What is frozen, and where

The frozen consumers live in the SDK repo under `consumer-fixtures/`:

- **`nextjs-starters-0.1.0-beta.2/`** — both example starters
  (`examples/nextjs-sdk` and `examples/nextjs-fixtures`) at the commit pinned
  when SDK 0.1.0-beta.2 shipped. The two starters share a lineage, so both
  pins are the same commit and the snapshot is one tree, kept verbatim
  (including the vendored fixtures tarball the starter consumed).
- **`mvr-like/`** — a minimal consumer whose `@kismet-tech/sdk`
  import/usage shapes are copied from the real MVR client: the server data
  plane (`createKismetClient`, `KismetApiError`), the Next guest-auth
  handlers and edge routes, the browser guest/auth clients, the contracts
  zod schemas, and the `Schema`/`GraphLinks` components. Content is
  placeholder only — never client content or secrets.

The check always swaps the SDK dependency for the HEAD-packed tarball; every
other pin in a frozen consumer (Next, React, the fixtures tarball) stays
exactly as released, so the run simulates a real client upgrading only the
SDK. The driver is `scripts/verify-consumer-upgrade.mjs`; the workflow is
`.github/workflows/sdk-consumer-upgrade.yml`. Unlike the examples job it
needs no deploy keys — the snapshots are plain directories in this repo.

## Reading a failure

- **`tsc --noEmit` fails** with missing or changed exports: a breaking change
  reached a surface a pinned client uses. The CHANGELOG's Breaking section
  and upgrade steps are the place to reconcile; if the break was accidental,
  restore the export.
- **`next build` fails**: the break is at module evaluation or render time
  rather than in types — often a changed runtime contract the type surface
  hides.
- **Deprecation warnings in the log are informational.** They name surfaces
  (such as `@kismet-tech/sdk/schema`) that pinned clients still import and
  that are scheduled for removal; they are tracked, not errors.

## Refreshing the snapshots at release

Refresh ONLY when a release is cut. The snapshots are previous-release
consumers, not moving targets:

1. At the release commit, resolve each example starter's new pin:
   `git ls-tree <release-sha> examples/` prints the two submodule SHAs.
2. From each starter repo, export the pinned tree wholesale:
   `git -C <starter-checkout> archive <pinned-sha> | tar -x -C consumer-fixtures/nextjs-starters-<version>/`
   Replace the old snapshot directory; keep it verbatim — no edits to starter
   code, no dependency bumps.
3. If the real client's import/usage shapes changed, copy the new shapes into
   `consumer-fixtures/mvr-like/` (placeholder content only, as always).
4. Update `consumer-fixtures/README.md`'s provenance table with the new
   starter SHAs and the released SDK version.
5. Run `node scripts/verify-consumer-upgrade.mjs` locally and confirm it
   PASSES before committing the refresh.

If a refreshed snapshot cannot pass against the new SDK, that is a real
client-upgrade break: fix the SDK or its documented upgrade path before
releasing. Never edit the frozen consumer to make the check green.
