Agent docs

AGENTS.md

Routing guide for any agent working in this repo.

You are in versable-builder: a private pnpm monorepo housing the Versable UI kit (@versable-git/ui), a frontend toolkit, the cross-app design canon, and a playground app that is both the component gallery and the docs browser (live at https://versable-builder-playground.vercel.app). Three apps consume the kit; the list with their pins is the first paragraph of CLAUDE.md and is kept true there, never restated here. None is upgraded until customer-facing work arrives (owner ruling 2026-08-17). This file is the map; explore from here. A person reading this repo starts at README.md instead; an agent putting the kit into an app starts at packages/ui/USAGE.md; this file is for an agent working inside this repo.

Ground rules (non-negotiable)#

  1. Run everything through fnm: fnm exec --using 24 -- pnpm <cmd> from the repo root. Plain pnpm/node resolves Homebrew Node 26 and breaks.
  2. Never kill or restart the server on :5104 (pm2 process playground-dev, hot-reloading, or playground-prod, a production build for review sessions; pm2 list says which is up). Never run pnpm build in apps/playground while playground-dev runs (build clobbers .next and kills it). If both are down, the cold-start lines are in CONTRIBUTING.md section 3. Switching one for the other is the owner's call.
  3. Semantic tokens only. pnpm lint:tokens (eslint) FAILS raw palette classes and bracket-arbitrary colors. Colors come from daisy semantic classes, the status vars (--ok-soft/--ok-line etc.), shadows from --shadow-*, and runtime data colors ONLY via accentStyle(color) + accent-* classes (packages/ui/src/internal/accent.ts).
  4. Don't fight daisyUI (owner rule). Extend it via theme vars and small CSS in packages/ui/theme/*.css; never rebuild a control it provides. Custom daisy themes REQUIRE the scale vars (--border, --size-selector, --size-field); they are set, do not remove them.
  5. Verify by running, not by reading. A change is done when the CI list passes locally (the six commands, verbatim in CONTRIBUTING.md section 3, "The bar for done") and the page renders and the interaction works: curl the route, drive one interaction (headless browser or fiber-snatcher fs from apps/playground/: fs navigate <path>, fs click "<text>", fs shoot). Screenshot claims require having looked at the screenshot.
  6. Commit per logical unit with descriptive messages. While parallel agents write, use PATH-SCOPED git add, never git add -A.
  7. Numbers in demo data are fictional; anything lifted from speedway reality gets scaled ×π (owner rule).
  8. Presets are shorthands, never gates (owner ruling 2026-08-14). A component preset bundles values its plain props can already express, and explicit props always win over it. A feature reachable ONLY through a preset is a design defect: build the prop first, then the preset over it. The pattern lives in Button (PresetProps + mergePresets) and Chip. A bundled field binds only where the component reads it (= preset.x, or a merge path like className's); TypeScript flags a misspelled bundle key but never an unwired one, so wire both ends when adding a preset field.

Repo layout#

PathWhat
packages/ui/src/The kit. One dir per component; index.ts is the barrel = the inventory. internal/ holds shared machinery (ClassNameMap, render-node, markdown-lite, accent, variants).
packages/ui/theme/The styling system: index.css (tokens, two daisy themes, REQUIRED scale vars), components.css (button rules incl. "shade" hovers), fields.css (input surfaces + field dials), accent.css.
packages/ui/docs/The contract doc per component: what it takes, sanctioned and banned combinations, "Before you adopt this", travels with, snippet. Read the one for any component you touch.
packages/ui/USAGE.mdThe consumer's door, human or agent: what you came for in one hop, the six steps to adopt the kit, the scope rule. Ships in the package.
apps/_templates/What you copy to start a new app, one folder per shape (Next, React Router SSR, Vite SPA), each with a TEMPLATE.md.
contract/A tenant, not a kit layer: the foundry contract (charter, system classes, wire contracts, guides, its own do-nots and ADRs) for everything below the screen. Served in the docs browser under Foundry contract; moves to versable-foundry in the org split (row #63). Nothing in CI reads it.
packages/toolkit/@versable-git/toolkit 0.1.0, published, framework-free utilities with tests; no app consumes it yet. PROPOSALS.md is where a cross-app helper is filed.
packages/qsync/@versable-git/qsync 0.1.0, published; URL-state sync the playground consumes.
.github/workflows/ci.yml (typecheck, lint:tokens, test, build, the citation ratchet, the do-nots diff), publish-kit.yml (publishes any of the three packages whose version the registry lacks), docs-publish.yml (mkdocs to tech.versable.ai).
docs/design-language/The visual law: 13 docs, each with principles (A), presets (B) and worked cases cited to file:line (C). Start at its README.md.
docs/app-patterns/How a screen is built and written up: data loading, the surface-doc template, the recipes (10-recipe-browsable-list.md, 11-recipe-record-detail.md), starting a new app, validating a UI change.
docs/DO-NOTS.mdEvery Banned rule in one generated page. Check it before you ship; regenerate with node scripts/build-do-nots.mjs after editing a contract doc.
contract/DO-NOTS.mdThe OTHER one, and a different subject. This repo now holds two generated DO-NOTS pages: docs/ is the kit and the screens, contract/ is what a module must do to be one. Different generators, different sources, neither reads the other. Regenerate the contract one with python3 contract/scripts/build-do-nots.py.
docs/GUIDEBOOK.mdHow the layers relate, the pin and upgrade rules, and the retrospective of lapses in the apps.
docs/SCRIPTURE.mdWhy the kit is shaped the way it is: intent, goals, non-goals, self-graded failure modes, success criteria.
docs/breakdowns/Six real screens taken apart layer by layer.
apps/playground/Next app. /components/* gallery (one page per component), /patterns/*, /docs/* the docs browser, /demo/* demo screens including /demo/admin. src/app/showcase-registry.ts is the one route registry; src/lib/docs-registry.ts is the docs allowlist.
docs/plan/Planning docs and the board (TRACKING.md). Owner rulings live in the numbered docs; older plans are under archive/.
docs/reference/The original design mocks. History, not law: the law is docs/design-language/.
docs/adr/Architecture decision records.
.claude/output/Generated studies and reports, gitignored. Never a source of truth; anything the repo must remember goes into a tracked doc.

Docs: read in this order for any kit or screen work (the governing copy; other docs carry scoped cuts and point here)#

  1. packages/ui/src/index.ts, the barrel. Every runtime export is named there; if a name is not on it, it does not exist.
  2. docs/design-language/README.md, then the doc for the trait you are touching (buttons and actions, status language, overlays, typography, and so on).
  3. The component's contract doc in packages/ui/docs/<component>.md, its Banned combinations and "Before you adopt this" first.
  4. The app-patterns recipe for the screen shape (10-recipe-browsable-list.md for lists, 11-recipe-record-detail.md for records, 05-starting-a-new-app.md for a new app).
  5. docs/DO-NOTS.md before you call anything done.

For orientation rather than a task: docs/GUIDEBOOK.md (how the layers relate) and docs/SCRIPTURE.md (why). Section 4 of the guidebook carries the pin and upgrade rules; read it against docs/plan/74-guidebook-conformance-audit.md, which records that its pin numbers are stale and that three apps consume the kit, not two.

Component conventions (match exactly when adding/emitting kit code)#

  • Props: className styles the component's main element; _className the outer wrapper; extra slots via <slot>ClassName (ClassNameMap).
  • Callbacks are value-first: onChange(value, item).
  • Variants/colors/sizes via Record lookups of STATIC class strings.
  • Icons via the registry: Icon="Search" (keys in icon/icon-for.ts; add new ones there from react-icons, never inline SVGs).
  • Every component: skeleton prop; most: loading, tooltip, noAnimate.
  • DataTable feature config: features={{ sort:{}, search:{fields}, filters:{registry}, selection:{}, pagination:{pageSize} }}; row menus {label, icon, onSelect, destructive?}.

When this file and CLAUDE.md disagree#

CLAUDE.md governs: it carries the owner's dated rulings and the bar for done, and is edited first. This file is the layout and the ground rules, and is brought into line with it.

When stuck / escalation#

Leave a dated line under "Open questions for the owner" in docs/plan/TRACKING.md describing what blocked you, commit what is green, never commit red typecheck or lint. Do not invent exceptions to the ground rules; stop and flag instead. A defect, a proposal or a question about the kit that came from an app (yours or a sibling's) is filed in docs/plan/INBOX.md, one entry, the shape is in the file; the review run reads it, you do not build from it.

@versable-git/ui · reference, canon, and method, read in place