# The consumer CI recipe (OUT-05)

A copy-pasteable, headless recipe that runs entirely in a consumer repo — no
`gen-ui-kit` access, no org auth, no interactive input. Four ordered steps:
preflight → structural lint → class-5 contract check → the render gate.

Every step obeys one exit contract: **0 = pass, 1 = findings/gate-fail, 2 =
setup/usage** — and a missing prerequisite is always red (never a silent
skip). See [`adia-preflight.mjs`](../../../scripts/adia-preflight.mjs) and
[`adia-contract-check.mjs`](../../../scripts/adia-contract-check.mjs) for the
two newly-shipped steps; `adia-lint` and `adia-probe.mjs` are documented in
this skill's own [SKILL.md](../SKILL.md).

## Minimum plugin version

Pin **`@adia-ai/adia-ui-factory@>=0.8.35`** — the first cut carrying both
gh#1041's `adia-lint` scope fix (commit `1b7fe9b27`, unreleased as of
2026-08-12) and this recipe's own scripts (gh#1136). On an older version, a
write under `node_modules/**` still produces `adia-lint` hook findings
(gh#1041's bug); the recipe below assumes it's fixed.

## GitHub Actions form

```yaml
name: surface-qa
on: [push, pull_request]
jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      # Advisory only (see "npm audit posture" below) — never gates the job.
      - run: npm audit --omit=dev || true
      - run: npx playwright install --with-deps chromium

      # step 0 — preflight: every prerequisite, remedy-named (REQ-02).
      # Exits 2 (red) on any missing prerequisite — never a silent skip.
      - run: node node_modules/@adia-ai/adia-ui-factory/scripts/adia-preflight.mjs --url http://localhost:4173

      # step 1 — structural lint, CLI mode: exit 1 on any finding.
      - run: node node_modules/@adia-ai/adia-ui-factory/scripts/adia-lint.mjs $(git ls-files 'src/**/*.js' 'src/**/*.html' 'src/**/*.css')

      # step 2 — class-5: authored attributes vs the shipped component contracts (REQ-03).
      - run: node node_modules/@adia-ai/adia-ui-factory/scripts/adia-contract-check.mjs src/

      # step 3 — headless render gate; exit 2 (no server / no playwright) is red too.
      - run: npm run preview &
      - run: npx wait-on http://localhost:4173
      - run: node node_modules/@adia-ai/adia-ui-factory/scripts/adia-probe.mjs http://localhost:4173 --selector my-app-shell --json > verify-proof.json
      - uses: actions/upload-artifact@v4
        with: { name: verify-proof, path: verify-proof.json }
```

## Bare `npm run` wiring

The equivalent as `package.json` scripts, for a consumer without GitHub
Actions (any CI, or a local pre-push hook):

```json
{
  "scripts": {
    "ui:preflight": "node node_modules/@adia-ai/adia-ui-factory/scripts/adia-preflight.mjs --url http://localhost:4173",
    "ui:lint": "node node_modules/@adia-ai/adia-ui-factory/scripts/adia-lint.mjs $(git ls-files 'src/**/*.js' 'src/**/*.html' 'src/**/*.css')",
    "ui:contract": "node node_modules/@adia-ai/adia-ui-factory/scripts/adia-contract-check.mjs src/",
    "ui:probe": "node node_modules/@adia-ai/adia-ui-factory/scripts/adia-probe.mjs http://localhost:4173 --selector my-app-shell --json",
    "verify": "npm run ui:preflight && npm run ui:lint && npm run ui:contract && npm run ui:probe"
  }
}
```

`npm run verify` runs all four steps in order and stops at the first
non-zero exit — the same ordering and short-circuit behavior as the GitHub
Actions job above.

## npm audit posture (advisory)

A fresh `adia-scaffold` output currently ships with 2 `npm audit` findings
(Stage-1 probe; root cause unconfirmed as of this writing). The recipe runs
`npm audit` as an **advisory** step (`|| true`) rather than a gate — gating
on it before the root cause is known would make the recipe red for reasons
this SPEC does not control. Do not remove the advisory step; do not
silently promote it to a hard gate without first closing the root-cause
investigation.

## What a green run actually proves (read before trusting CI)

**A green run is the mechanized half of a VerifyProof, not the whole
thing.** `adia-probe.mjs --json`'s `imageRead` slot is recorded as
`"REQUIRED — a screenshot nobody reads verifies nothing"` in every proof —
CI cannot mechanize that slot, so it stays `pending` in the uploaded
artifact regardless of how green every other gate is. A CI badge, a commit
status, or a docs page that presents this recipe's green as a *complete*
VerifyProof — without naming the pending image read — is over-claiming
(R1's named top failure: documented-unverified inflation). State it
alongside the badge: "CI verifies the mechanized gates; the screenshot
still needs a human or model read before this ships."

## Known limitation

`adia-contract-check.mjs`'s existence check reads the shipped
`custom-elements.json` manifest as its source of truth (REQ-03) — that
manifest itself is currently missing a small number of real properties for
14+ tags (tracked as gh#1154, e.g. `icon-ui`'s `tone`). Until gh#1154 lands,
the class-5 step can false-flag those specific attributes as dead; check
the linked issue (or the tag's own `references/authoring-components.md`
lineage) before assuming a flagged attribute is truly unsupported.
