# Step 5 — The ship gate

The project ships with its project-local artifacts — `design.md` (the resolved fork, with provenance line, brief bindings, recorded deviations), its derived `design.tokens.css`, and `product.md` — and its memory server-side. The gate has two tiers, both objective: this is the only runtime check the skill performs, permitted precisely because it is automated and needs no judgment. Nothing here grades taste — anything that needs grading was the build-time harness's burden, not the run's.

## Tier 1 — fails closed

Every check a binary predicate. Run the validator (`node scripts/validate-design-md.mjs --project . --mode ship`) plus the build itself; all of these must hold:

- **No placeholder text or scaffold stubs** anywhere (lorem, TODO, "Coming soon", empty `href="#"` walls).
- **Data plausibility** against the corpus's declared invariants: aggregates reconcile with their visible rows; legal ranges hold; no degenerate repetition in visible rows (surname clusters, identical amounts).
- **Accessibility floor, mechanical**: contrast computed on the emitted token pairs in *both* themes; focus visible on every interactive element; `prefers-reduced-motion` respected.
- **Every declared surface reachable** — each surface in the structure doc has a live route/nav path.
- **The project fork resolves** — `--project .` finds `design.md` at `packages/common/` in a multi-package workspace or `src/common/` in a single-package repo, and a copy anywhere else fails the resolution — and the token artifact recompiles from it to a byte-match (the validator's drift check).
- **The pipe checks**:
  - the token artifact carries 100% coverage and components consume tokens — no raw color/size literals in component code;
  - fonts bound to installed packages under registered family names (a family that never resolves is a fail);
  - every authored token group reaches a named consumer — an emitted ramp nobody consumes is a fail;
  - the emitted CSS actually compiles;
  - charts and status surfaces consume the package's tokens (a dark dashboard with hardcoded light charts fails here).
- **The library reach checks** (`primitives: nurix`):
  - `personality.css` recompiles from the design.md's personality block to a byte-match (the validator's `dmv-12`); a design.md with no block fails nothing, and its `dmw-05` warn appears in the ship report;
  - no `data-theme` selector or attribute in app CSS or HTML — the theme is the `.dark` class;
  - no `duration-<n>` utility, no `transition-all`, and no literal `ms` inside a transition or animation declaration in app code — the seam carries timing;
  - no `<time` element outside `<Timestamp>`; no `truncate` or `line-clamp-` utility (the `clip` family replaces them);
  - every interactive element under 24px in either rendered dimension carries a `hit-area` utility.

A Tier-1 failure blocks the ship; fix and re-run. Binary phrasing is the contract — anything that would need "use sparingly" grading does not belong in this tier.

## Tier 2 — flags, never gates

Sweep `data/tells.csv` (the operator tell list), at this step only, plus the motion tells (`transition: all`; `ease-in` on an entrance; animation attached to a keyboard-triggered action). Under `nurix`, the reach tells join them: a `p-*` / `gap-*` literal on a collection container; a relative-time formatter in app code; a `fetch` inside an effect feeding a `<Select>`; a repeated-row container without `data-mode="inline"`; a `success` / `warning` / `info` hue declared anywhere but `design.md`. Flags cannot fail the build and trigger no in-run judging: each is **fixed or dismissed with one recorded line** through `record`, and every flag appears in the ship report for the human's eyes — numbers point eyes; they do not replace them. The validator may run this sweep warn-only after compose-step edits; nothing new ever fails the build mid-composition.

## The ship report

Close the run with a compact report: surfaces shipped, the recorded choices, Tier-1 results, every Tier-2 flag with its disposition, and the validator's warn list. No scores, no self-grades. Write it to memory — `record` with `report: { kind: "gate", summary }` — and, if the run's surviving edits changed the project's `design.md`, push the new text in the same `record` so the server canonical stays current.
