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 <Demo type="…" /> in the other cell of
a <Grid variant="borderless">. 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.
Demopicks frommdx-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 fromdemo/cell.tsx, theSlack*parts fromdemo/slack.tsx, the block compositions indemo/status-blocks.tsxanddemo/subscribe.tsx, and the@openstatus/uistatus blocks. A repeated visual is a new part in one of those files, never atitle/itemsprop or a copied class stack. - A
<Demo>is a picture, so the<SrOnly>block next to it says what it shows: visually hidden on the page, read by screen readers, plain copy in the.mdrepresentation (convert.tsunwraps it). The lint requires one in every section that holds a demo. Its numbers come fromdata/demo-data.ts, like the demo's do. Search skips it, since a hit would highlight nothing. - One data file per concern.
data/customers.tsfeedsLogoCloudandQuote(the/customerslisting comes frompages/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.auditis the timeline of record, and every timestamp in a demo comes fromauditRow().--radiusis 0 on this site, so arounded-*class in a demo is dead code. - No raw
classNameinpages/. A CTA row is<Actions source="…">, which appends the trackingrefto 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
.darksibling.