# Integrating a site with Sonor — agent guide

> **You installed `@sonordev/site-kit`.** This file tells a coding agent how to
> wire a Next.js site to Sonor correctly and prove it works — no web access
> needed. **Editing site-kit itself?** See `CONTRIBUTING.md`.

For the full machine-readable contract (modules, env, blessed patterns, failure
modes, exit codes) run:

```bash
npx sonor-setup manifest --json
```

Everything below is also in that manifest. This file is the human-skimmable version.

## North star

Take a bare Next.js repo → a fully wired, **verified-green** Sonor site. The
signal that you are done is `verify` exiting 0 — not "the command ran."

## Canonical sequence

```bash
npx sonor-setup manifest --json                              # discover the package
npx sonor-setup init --api-key "$SONOR_API_KEY" --yes --json # wire layout + .env.local + postbuild
npx sonor-setup scaffold --yes --json                        # sitemap, robots, llms, proxy
npx sonor-setup mcp --yes --json                             # agent tools: MCP endpoint, card, relay (optional)
# Existing 2.x-6.x site? migrate deterministically:
npx sonor-setup codemod --check --json                       # exit 1 ⇒ work pending
npx sonor-setup codemod --write --json                       # apply (idempotent, minimal diffs)
next build                                                   # you run the build
npx sonor-setup verify --json                                # exit 0 === done
```

## The machine contract

- **Every agent-native command supports `--json`** and prints exactly ONE JSON
  envelope to stdout (all human logs go to stderr). Parse stdout; branch on
  `exitCode` / `ok` / `checks`.
- **Exit codes:** `0` OK · `1` FAILED (result is red, fix the code) · `2` USAGE
  (bad invocation) · `3` CONFIG (missing input — the error names the flag) · `4`
  NETWORK (API/key) · `5` INTERNAL.
- **Never interactive under `--json`, `--yes`, or a non-TTY.** A command that
  needs input exits `3` with an actionable `fix` instead of hanging.
- Each `checks[]` finding has a stable `id`, a `fix`, and often a `fixCommand`
  you can run directly.

## The one env var

The **only** variable a site needs is:

```bash
SONOR_API_KEY=sonor_1a2b3c4d_...   # server-side only — NO NEXT_PUBLIC_ prefix
```

`SiteKitLayout` reads it server-side and derives the project id + all client
config. **Never** add `NEXT_PUBLIC_SONOR_API_KEY`, `UPTRADE_API_KEY`,
`NEXT_PUBLIC_UPTRADE_API_KEY`, or `SONOR_PROJECT_ID`. Uptrade keys/vars are fully
deprecated. Key format: `sonor_{project-uuid-first8}_{secret}`.

## Blessed patterns

**Root layout — `SiteKitLayout` (a server component), wrap `{children}`:**

```tsx
import { SiteKitLayout } from '@sonordev/site-kit/layout'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <SiteKitLayout>{children}</SiteKitLayout>
      </body>
    </html>
  )
}
```

Use `SiteKitLayout`, **never** the deprecated `SiteKitProvider` (it forces the
whole tree client-side and breaks RSC/SSR). `codemod` migrates it for you.

**Analytics is already a deferred, childless sibling. Don't wrap `{children}`,
and don't hand-roll it.** Since 3.0.2, `SiteKitLayout` renders `{children}`
first and mounts analytics, chat, popups and the fleet heartbeat after it, deferred
to window load + idle (or the first interaction). 4.0.0 removed the last
wrappers. So the plain layout above is the whole pattern: configure analytics
with `analytics={{ ... }}`, and track custom events with the standalone
`trackEvent` / `trackConversion` from `@sonordev/site-kit/analytics` (no
provider needed; `useAnalytics()` throws outside one).

What still de-opts a statically prerenderable route to 100% client rendering
(the hero paints only after hydration; mobile LCP tanks) is a component *you*
wrap around `{children}` that skips server rendering, usually
`next/dynamic({ ssr: false })`. The old `SiteKitLayout analytics={false}` plus
a hand-rolled deferred sibling was the workaround for site-kit <3.0.2. On those
installs, upgrade.

**Agent tools — the built-in Sonor MCP server (7.0).** Don't hand-write
business-info, FAQ or review tools; `npx sonor-setup mcp` wires
`sonorMcpServer` from `@sonordev/site-kit/mcp/sonor`, and a site's own tools
go in its `tools` array. `send_inquiry` needs the person's go-ahead
(`person_confirmed: true`) and a form with "Agent inquiries" on in Sonor.
Pass `onToolCall: reportToolCallsToSonor()` to `createMcpHandler` so Sonor
sees which agents called. A site that already runs its own MCP server is a
custom implementation: `sonor-setup mcp` leaves it alone,
and so should you; add reporting to it rather than replacing it.

**Imports:** from a module's entry (`@sonordev/site-kit/sync`,
`/website/images`, `/seo/llms`), never the root, which is types-only. The
package is ESM only.

## Common failure modes → the check that catches it

| Symptom                                                                                             | Cause                                                                                                                                                                                                | Fix                                                                                                    | `doctor`/`verify` check             |
| --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------- |
| Mobile LCP \~4s; server HTML is an empty shell + `self.__next_f` flight data, no `<section>`/`<h1>` | Something wraps `{children}` in a component that skips SSR, usually `next/dynamic({ ssr: false })` (e.g. the site's own lazy Providers) → 100% CSR. Never a plain `SiteKitLayout` on site-kit ≥3.0.2 | Keep the layout. Import that wrapper statically or mount it as a childless sibling. On <3.0.2, upgrade | `ssr.render`                        |
| Build throws "runtime is not available in proxy" / codemod skips middleware.ts                      | Next 16's `proxy.ts` always runs on Node and rejects a `runtime` export; `middleware.ts` still accepts it                                                                                            | Remove the `runtime` export, then `npx sonor-setup codemod --write` moves the file to `proxy.ts`       | `middleware.netlify`                |
| Invalid-key console spam; data features paused (401/403)                                            | Stale/rotated key, or an env change wasn't redeployed                                                                                                                                                | Put the current `sonor_` key in `.env.local` and redeploy                                              | `key.valid`                         |
| Deprecated `SiteKitProvider` in layout                                                              | 2.x integration                                                                                                                                                                                      | `npx sonor-setup codemod --only provider-to-layout --write`                                            | `layout.sitekit`                    |
| `@uptrademedia/site-kit` / `UPTRADE_*` remnants                                                     | pre-rebrand site                                                                                                                                                                                     | `npx sonor-setup codemod --only uptrade-to-sonor --write`                                              | `package.installed` / `env.api-key` |
| Hero present but LCP late                                                                           | above-the-fold element is `opacity:0` + JS entrance-animated                                                                                                                                         | Render hero fully static; gate reveal animations below the fold                                        | —                                   |

## Verifying (definition of done)

```bash
npx sonor-setup verify --json              # static health + key validity + SSR (from .next build)
npx sonor-setup verify --url https://your-site.com --json   # SSR check against a live/preview URL (strongest)
```

`verify` exits `0` only when the integration is genuinely green. If it exits
non-zero, read `checks[]` — each red check carries a `fix` (and often a
`fixCommand`), and `nextSteps[]` names what to run. Loop until green.
