# AGENTS.md — apps/web ## `.well-known` routes `apps/web/src/app/.well-known/` is a dot-directory, and TypeScript's `include` wildcards skip those. Files under it are outside the tsconfig program, so path aliases (`@/…`) do not resolve and nothing there is type-checked as part of the app. Use relative imports (or node built-ins, as `.well-known/agent-skills/index.json/route.ts` does) and check the output by requesting the route. ## Search The ⌘K search is homegrown: the ranker lives in `apps/web/src/app/api/search` and the index in `apps/web/src/content/utils/search-index.ts`. Do not reach for Pagefind, Orama or Algolia. ## Content pages Content pages are MDX prose built from the existing components (`Grid`, `Details`, …). Reach for those before inventing a bespoke layout. Never link out to a competitor. Name them as plain text — an external link donates domain authority and leaks the conversion. The home and product pages (`pages/home.mdx`, `pages/product/*.mdx`) follow one section pattern: an `h2` outside the grid, a text cell with two sentences and a short list of internal links, and one `` in the other cell of a ``. `content-lint.test.ts` enforces the structural rules on those pages (registered tags, `SrOnly` next to every demo, no raw `className`, every demo type in the kitchen sink); the rest of the rules below are kept by hand and in review. `/kitchen-sink` (noindex) renders every component once so drift is visible. - Props are enums, not free JSX. `Demo` picks from `mdx-components/demo/index.tsx`; a new demo is a new key there, reviewed in a PR. MDX never composes status blocks by hand. - Demos are compositions, not prop bags. They nest the `Cell*` parts from `demo/cell.tsx`, the `Slack*` parts from `demo/slack.tsx`, the block compositions in `demo/status-blocks.tsx` and `demo/subscribe.tsx`, and the `@openstatus/ui` status blocks. A repeated visual is a new part in one of those files, never a `title`/`items` prop or a copied class stack. - A `` is a picture, so the `` block next to it says what it shows: visually hidden on the page, read by screen readers, plain copy in the `.md` representation (`convert.ts` unwraps it). The lint requires one in every section that holds a demo. Its numbers come from `data/demo-data.ts`, like the demo's do. Search skips it, since a hit would highlight nothing. - One data file per concern. `data/customers.ts` feeds `LogoCloud` and `Quote` (the `/customers` listing comes from `pages/customers/*.mdx`). `data/demo-data.ts` (one fictional company, one incident) feeds every demo, so every demo on every page tells the same story; `demo.audit` is the timeline of record, and every timestamp in a demo comes from `auditRow()`. `--radius` is 0 on this site, so a `rounded-*` class in a demo is dead code. - No raw `className` in `pages/`. A CTA row is ``, which appends the tracking `ref` to app links; never hand-write `?ref=`. - Every capitalised tag must be registered in `mdx-components/index.tsx`. - No new colours beyond tokens, no font sizes beyond the prose scale, no radius. Dark mode comes from tokens only; images get a `.dark` sibling.