Standards
Single source of truth for every agent, LLM, and contributor in this repo. All agents inherit these defaults. When an agent’s body contradicts this file, this file wins — open a PR to update the agent body.
⚠ Read this first: ADR-026 — LLM Agent Token Usage & Search-First Workflow. Search before you read. Find the relevant
llmPrompt(ADR-021) before implementing. Use design-system metadata as the routing index.
LLM quick-read: Read §1 (Precedence) first — it resolves all conflicts. Then §3 (Tech Stack) and §4 (Skill Routing). For component work, use Storybook/MCP documentation and
docs/adrs/docs-index.jsonfor ADR routing. For ANY session, read ADR-026 first — it explains the search-first workflow and the metadata you should route through.
Principle:
STANDARDS.mdpoints. ADRs explain. Skills execute. Examples demonstrate.
1. Instruction Precedence
When instructions conflict, follow this order (highest → lowest authority):
- Direct user request — as long as it does not violate repo safety rules
- Security, privacy, and production safety rules — non-negotiable
- This file (STANDARDS.md)
agents.md— design-system operating rules- Accepted ADRs — architectural decisions
- Relevant skill file (
.copilot/skills/<name>/SKILL.md) AI_RULES.md— project-level hard rules- Local README or feature docs
- Existing nearby code patterns
Exception: A more specific accepted ADR may override a general rule in STANDARDS.md, but it must explicitly say so in its “Decision” section.
Always-read ADRs (read before any task work):
- ADR-026 — LLM Agent Token Usage & Search-First Workflow — how to use tools, when to fetch more vs. less, where the routing metadata lives.
- ADR-021 — LLM Prompts Per Component — every component carries a hand-written
llmPrompt; read it before integrating that component.
2. Canonical MCP Tool Set
Every agent that operates inside this repo SHOULD have access to and SHOULD prefer:
tools: # VS Code / workspace - vscode/extensions - vscode/getProjectSetupInfo - vscode/installExtension - vscode/memory - vscode/newWorkspace - vscode/resolveMemoryFileUri - vscode/runCommand - vscode/vscodeAPI - vscode/askQuestions # Terminal / execution - execute/getTerminalOutput - execute/killTerminal - execute/sendToTerminal - execute/createAndRunTask - execute/runInTerminal - execute/runNotebookCell - read/terminalSelection - read/terminalLastCommand - read/getNotebookSummary - read/problems - read/readFile - read/viewImage # Agents - agent/runSubagent # Browser - browser/openBrowserPage - browser/readPage - browser/screenshotPage - browser/navigatePage - browser/clickElement - browser/dragElement - browser/hoverElement - browser/typeInPage - browser/runPlaywrightCode - browser/handleDialog # Editing - edit/createDirectory - edit/createFile - edit/createJupyterNotebook - edit/editFiles - edit/editNotebook - edit/rename # Search - search/changes - search/codebase - search/fileSearch - search/listDirectory - search/textSearch - search/searchSubagent - search/usages # Web - web/fetch - web/githubRepo # dmwd-io design system MCP (primary UI source of truth) - dmwd/get-documentation - dmwd/get-documentation-for-story - dmwd/get-storybook-story-instructions - dmwd/list-all-documentation - dmwd/preview-stories # Misc - todoDomain-specialist agents MAY append extra tools after the canonical list. They MUST NOT remove dmwd/* tools — the design system MCP is always required.
3. Default Tech Stack
First action for any choice not in this section: check
DECISION_DEFAULTS.mdbefore reasoning from scratch.
Open-source first. Default to OSS or source-available tools. Never recommend closed/SaaS-only tools without flagging the tradeoff.
Design System (Layer 0 — always start here)
- Component library:
@dmwd-io/design-system— 200+ components, Storybook MCP-accessible - First action for any UI task: query
dmwd/*MCP tools to find existing components before writing any UI code - Styling: Tailwind CSS via the design system’s Tailwind preset — never add raw Tailwind classes that bypass design tokens
- Variants:
class-variance-authority(CVA) +clsx+tailwind-merge - Icons: Lucide React (already in the design system)
- Component dev: Storybook (the design system IS the Storybook catalog)
- Storybook IA: Story titles must use an existing root section from ADR-016 §0. Never create new root sections without updating ADR-016.
- Visual regression: Lost Pixel — NOT Chromatic (closed SaaS)
Frontend Framework
- Language: TypeScript (strict mode,
exactOptionalPropertyTypes: true) - Framework: React 19
- Meta-framework: Astro (when SSG/island architecture fits) — also default backend if full-stack TS
- Routing: TanStack Router (file-based for >10 routes)
- Server state: TanStack Query (never store server data in Zustand)
- Tables / data grids: TanStack Table (already wrapped in
DataGridcomponent — use that first) - Forms: React Hook Form + Zod resolver (or TanStack Form when paired with Router/Query/Table)
- Validation / schemas: Zod — single source of truth shared between client and server
- Local UI state: Zustand (slices, never one mega-store)
- Animation: Motion (formerly Framer Motion) — sparingly, prefer design system motion tokens
- Testing: Vitest + React Testing Library; Playwright for E2E
- Linting / formatting: Biome (replaces ESLint + Prettier — single binary, fewer configs) — existing projects keep their current linter
- Build: Vite (or Astro’s built-in)
- Package manager: pnpm
- Monorepo: Turborepo (preferred) or Nx
Backend
- Primary (TS): Astro (server endpoints) or Hono if standalone API
- Primary (Go): Fiber + GORM + Zap + OpenTelemetry, OpenAPI generated via oapi-codegen
- Auth: Better-Auth (TS) / OIDC (Go)
- Background jobs: River (Go) / BullMQ (TS)
- Realtime: WebSockets via native server, or Centrifugo at scale
Data
- Primary RDBMS: PostgreSQL (CloudNativePG operator in k8s)
- Embedded / local: SQLite (libSQL / Turso self-host when distributed)
- Migrations: Atlas (preferred — OSS, declarative) or Drizzle Kit (TS) / goose (Go)
- Query builder / ORM (TS): Kysely (preferred — type-safe SQL, no magic), Drizzle (second choice), Prisma only when team strongly prefers ergonomics over control
- Cache / queue / pubsub: Valkey (Redis fork — fully OSS under Linux Foundation)
- Search: Meilisearch (small/medium) or Typesense (large/multi-tenant)
- Object storage: S3-compatible — MinIO (single tenant) or Garage (multi-region OSS)
- Vector / AI: pgvector (in Postgres) — avoids a separate vector database
Infrastructure & Delivery
- Containers / Orchestration: Docker, Kubernetes
- CI/CD: GitHub Actions
- GitOps: ArgoCD + Helm
- Secrets: 1Password Connect
- Local dev: Skaffold + Task
Observability (all OSS, all self-hostable)
- Logs: Zap (Go), pino (TS) → Loki
- Metrics: Prometheus + Grafana
- Traces: OpenTelemetry SDK → Tempo
- Errors: GlitchTip (Sentry-protocol-compatible, fully OSS) — drop-in for Sentry SDKs
- Uptime: Uptime Kuma
- SLO framework: Sloth (generates Prometheus recording + alerting rules from SLO YAML)
- Profiling: Pyroscope (continuous profiling)
Standard Additions at Scale (all OSS unless flagged)
| Category | Default Choice | Notes |
|---|---|---|
| Feature flags | OpenFeature SDK + GrowthBook (self-host) | Vendor-neutral interface |
| Product analytics | PostHog (self-host) | Events, funnels, session replay, surveys |
| Transactional email | Postal (self-hosted SMTP) + React Email | Listmonk for newsletters. Avoid Resend (closed SaaS) unless explicitly chosen |
| Payments / billing | Lago (OSS metering) + Killbill (subscriptions) | Payment gateway (card processing) is the one unavoidable closed dependency — flag it, default to Stripe only if user accepts |
| i18n | Lingui (TS) or go-i18n (Go) | - |
| Docs site | Astro Starlight (preferred) or Docusaurus | - |
| API contracts | tRPC inside monorepo, OpenAPI at the edge | hono-openapi or zod-to-openapi for TS; oapi-codegen for Go |
| API mock | Prism (OpenAPI mock server) | Parallel FE/BE work |
| Workflows | Temporal (self-host) for durable workflows, XState for in-process | - |
| Job scheduling | River (Go), BullMQ (TS) | Both Postgres/Redis-backed OSS |
| Container registry | Harbor (self-host) or GHCR | - |
| Image building | ko (Go, zero-Dockerfile) or multistage Dockerfile + buildx | - |
| k8s policy | Kyverno (preferred — pure YAML) or OPA Gatekeeper | - |
| Service mesh (only when actually needed) | Linkerd (lightweight, Apache 2) | - |
| Backups | Velero for cluster, pgBackRest for Postgres | - |
| Internal tools / admin UI | Refine (OSS, React + design system) | - |
| CMS (when needed) | Payload (Node, OSS) or Directus | - |
| Auth (self-host SSO) | Authelia or Authentik for OIDC; Better-Auth stays default for app-level auth | - |
| Status page | Cachet or Statping | - |
4. Skill Routing
Agents SHOULD invoke skills rather than reinventing them. Map user intent → skill:
| Intent / Trigger Phrase | Skill |
|---|---|
| ”design tokens”, “color palette”, “typography scale”, “developer handoff” | ui-design-system |
| ”build component”, “make it look better”, “production-grade UI”, “avoid AI slop” | frontend-design |
| ”favicon”, “PWA icons”, “open graph image” | web-asset-generator |
| ”logo from clearbit/logo.dev” | logo-dev-automation |
| ”design schema”, “ERD”, “normalize”, “migration plan”, “index review” | database-designer |
| ”SLI/SLO”, “alerting strategy”, “dashboard generator” | observability-designer |
| ”threat model”, “STRIDE”, “OWASP review”, “secrets handling” | senior-security |
| ”CI pipeline”, “deploy”, “infra as code”, “containerize” | senior-devops |
| ”system design”, “ADR”, “microservices vs monolith”, “scale plan” | senior-architect |
| ”test strategy”, “Playwright setup”, “coverage analysis” | senior-qa |
| ”scaffold fullstack”, “Next/FastAPI/Django stack” | senior-fullstack |
| ”React component”, “hooks”, “TanStack Query/Table” | senior-frontend |
| ”tech debt”, “remediation”, “complexity report” | tech-debt-tracker |
| ”compare frameworks”, “TCO analysis”, “stack evaluation” | tech-stack-evaluator |
| ”PRD”, “product spec”, “requirements doc” | prd-generator |
| ”user story”, “acceptance criteria”, “sprint plan” | agile-product-owner |
| ”RICE”, “interview synthesis”, “discovery framework” | product-manager-toolkit |
| ”OKRs”, “vision”, “competitive analysis” | product-strategist |
| ”churn risk”, “expansion”, “customer health” | customer-success-manager |
| ”board deck”, “investor update”, “exec strategy” | ceo-advisor |
| ”tech strategy”, “team scaling”, “DORA metrics” | cto-advisor |
| ”create new skill”, “improve a skill” | skill-creator |
| ”organize files”, “find duplicates” | file-organizer |
| ”user research synthesis”, “usability test plan” | ux-researcher-designer |
| ”design API”, “OpenAPI spec”, “tRPC router”, “REST endpoints”, “API versioning” | api-designer |
| ”build operator”, “CRD”, “controller-runtime”, “kubebuilder” | kubernetes-operator |
| ”app is slow”, “lighthouse”, “LCP”, “INP”, “bundle size”, “react slow” | react-perf-auditor |
| ”astro app”, “client:load”, “islands”, “content collections” | astro-architect |
| ”zod schema”, “shared types”, “z.infer”, “contracts package”, “discriminated union” | zod-schema-architect |
| ”tanstack router”, “file-based routes”, “search params”, “type-safe routing” | tanstack-router-architect |
| ”new go service”, “fiber api”, “go template”, “scaffold go” | golang-fiber-bootstrapper |
| ”slow query”, “explain analyze”, “index strategy”, “pg_stat_statements” | postgres-performance |
| ”github actions”, “reusable workflow”, “OIDC”, “matrix build”, “branch protection” | github-actions-architect |
| ”open a PR”, “PR description”, “self-review”, “code review” | pr-review-checklist |
| ”monorepo”, “turborepo”, “nx”, “pnpm workspace”, “shared package” | monorepo-architect |
| ”incident”, “outage”, “SEV1”, “postmortem”, “on-call”, “rollback now” | incident-commander |
| ”design system component”, “build a component”, “Storybook story”, “component variant” | dmwd-component-author |
| ”CRUD page”, “list + detail + form”, “resource feature” | crud-recipe |
| ”dashboard”, “analytics overview”, “KPI grid” | dashboard-recipe |
| ”add logging”, “request context”, “pino setup”, “zerolog”, “wide events” | logging-setup |
| ”draft a demand letter”, “cease and desist”, “motion”, “contract”, “NDA” | legal-document-drafter |
| ”legal template”, “court PDF”, “certificate of service”, “pleading layout” | legal-pdf-layouts |
How to invoke
Detect a trigger phrase → load the matching skill from .copilot/skills/<name>/SKILL.md → follow its workflow exactly. Don’t paraphrase; execute it.
For the full skill index (status, related ADRs, when NOT to use each skill), see .copilot/skills/README.md.
5. Agent Authoring Rules
Every agent in this repo MUST follow these rules. Full contract in .copilot/skills/README.md.
- One job, done well. No agent should mention more than 2 sibling agents by name.
- Required frontmatter:
name,description,tools(from §2 canonical list) - Required sections (in order): Identity, When to use me, When NOT to use me, Default tech stack, Skills I invoke, Workflow, Output contract
- No prose duplication. If it’s in
STANDARDS.mdoragents.md, link to it. - Be terse. No agent body exceeds ~400 lines.
- No
function Demo()inside render callbacks — see ADR-017.
Skill descriptions, when-to-use details, and ADR links live in .copilot/skills/README.md and individual SKILL.md files — not here.
6. Engineering Standards
Full standard:
docs/adr-024-engineering-standards.mdx
Floors for all new code:
| Rule | Limit |
|---|---|
| File size | ≤150 lines |
| Function size | ≤70 lines |
| Dependencies | Always injected |
| Config | Env vars or config file (Zod at startup in TS; Viper in Go) |
| Type safety | Mandatory — no any, no untyped interface{} |
| Naming | Verbose: generateAcmeInvoicePDF() not fn() |
- TDD: RED → GREEN → REFACTOR; 50% coverage minimum; tests offline only
- Task runner: Taskfile (not Make). Standard targets:
dev lint test test-cover build migrate seed - Commits: conventional format, body required, ≤72 char title, imperative mood, refs attached
- Migration: apply incrementally when touching files — no big-bang rewrites
6a. Go Standards (addendum to §6)
These apply to all Go services, CLIs, and tools. They supplement the language-agnostic rules above.
Project layout — always cmd/ internal/ pkg/:
myservice/ cmd/server/main.go # entry point only — parses config, wires DI, calls Run() internal/handler/ # HTTP handlers (Fiber) — never import from outside internal/service/ # business logic — interfaces first internal/repository/ # database access — interface + GORM implementation internal/provider/ # vendor SDK wrappers (auth, email, storage, AI) pkg/ # packages safe to import by other modules Taskfile.yml .golangci.ymlError handling:
- Wrap errors with context:
fmt.Errorf("user.Create: %w", err)— neverpanicin library code - Return
(T, error)— never swallow errors - Map domain errors to HTTP error codes in the handler layer only
- Use the standard error envelope:
{ "error": { "code", "message", "request_id" } }
Config and secrets (Viper):
type Config struct { Port int `mapstructure:"PORT"` DBUrl string `mapstructure:"DATABASE_URL"` APIKey string `mapstructure:"API_KEY"` // read once; never logged}
func Load() (Config, error) { viper.AutomaticEnv() viper.SetConfigFile(".env") _ = viper.ReadInConfig() // env vars always win var c Config return c, viper.Unmarshal(&c)}Interface-first DI:
// Define interface in the consumer packagetype UserRepository interface { Create(ctx context.Context, u User) (User, error) GetByID(ctx context.Context, id string) (User, error)}
// Wire in main.go / cmd package — not inline in handlersrepo := repository.NewGORMUserRepository(db)svc := service.NewUserService(repo, logger)h := handler.NewUserHandler(svc)Logging (zerolog):
log.Info(). Str("event", "http.request_completed"). Str("request_id", requestID). Int("status_code", c.Response().StatusCode()). Int64("duration_ms", time.Since(start).Milliseconds()). Msg("request completed")Linting: golangci-lint run with .golangci.yml (shadow, errcheck, exhaustive, govet at minimum).
Taskfile standard targets:
tasks: dev: { cmds: [go run ./cmd/server] } lint: { cmds: [golangci-lint run ./...] } test: { cmds: [go test -race ./...] } test-cover: { cmds: [go test -race -coverprofile=coverage.out ./...] } build: { cmds: [go build -o dist/server ./cmd/server] }6b. Doc-Comment Standard (all languages)
This rule is non-negotiable. Applies to every exported function, class, type/interface, hook, and component across TypeScript, JavaScript, Go, and Python.
Every exported symbol MUST carry a doc comment that conveys its contract without requiring the reader to read the body. State:
- One-line purpose — what it does, not how
- Each parameter — type, shape, meaning, and constraints
- Return value — what it is and when it is null/undefined/zero
- Notable side effects / thrown errors — network calls, mutations, exceptions that can escape
- When to use and when NOT to use — required for components and public APIs
- Governing ADR(s) — link the ADR that constrains this symbol’s design
Describe intent and contract, not a line-by-line restatement of the body. Comments go on declarations. Do not litter function bodies with noise.
Language conventions:
- TypeScript / JavaScript: TSDoc/JSDoc (
/** ... */) - Go: idiomatic Go doc comments (
// FuncName ...) on all exported identifiers - Python: PEP 257 docstrings (
"""...""")
Canonical TSDoc pattern to copy:
/** * Paginated data table with sort, filter, row-selection, and mobile card fallback. * * Use for collections that need server-driven sort/filter/pagination. For a simple * read-only list, prefer StackedList. For compact inline summaries, prefer MiniDataTable. * * @typeParam T - Row shape; must include a stable `id`. * @param columns - Column defs; `accessor` keys must exist on T. * @param data - Current page of rows. Component does not fetch — pass server data in. * @param pagination - Page state + onChange; omit to render unpaginated. * @param onSelectionChange - Fired with selected row ids; omit to disable selection. * @returns Accessible table element (role="grid"); collapses to cards below `md`. * @throws Never throws; invalid columns render an EmptyState, not an error. * * @remarks Governed by ADR-040 (CRUD), ADR-049 (Tables), ADR-006 (semantic colors). * @example * <DataGrid * columns={userColumns} * data={users} * pagination={{ page, pageSize, total, onChange: setPage }} * /> */export function DataGrid<T extends { id: string }>(props: DataGridProps<T>) { ... }Enforcement: TSDoc violations are flagged in PR review. Components shipped without doc comments on their props interface and function declaration will be bounced.
Full standard:
docs/adr-023-observability-and-wide-events-logging.mdx(ADR-023)
One log entry = one meaningful operation. All context attached. All fields queryable.
Canonical HTTP request completion event:
{ "timestamp": "2026-05-22T14:24:03.182Z", "level": "info", "msg": "request completed", "event": "http.request_completed", "service": "billing-api", "version": "1.4.2", "env": "production", "request_id": "req_123", "trace_id": "trace_abc", "span_id": "span_def", "user_id": "user_456", "tenant_id": "tenant_789", "method": "POST", "route": "/api/invoices", "status_code": 201, "duration_ms": 143, "data": { "invoice_id": "inv_123" }}Required fields (always present): timestamp level msg event service version env
Request context (when request exists): request_id trace_id span_id method route status_code
User context (when resolved — opaque IDs only, never PII): user_id tenant_id
Operational (when relevant): duration_ms error data
msg is static and grep-stable. Dynamic values go in fields, not in msg.
event is dot-namespaced and machine-readable: "billing.subscription_created".
Libraries: pino (TS), zerolog (Go), structlog (Python). See ADR-023 for idiomatic setup.
Never log: PII, secrets, payloads >2KB, stack traces at top level, interpolated msg strings.
8. Observability Concepts — What Goes Where
Do not conflate these. They have different retention, audiences, and tools.
| Concept | Purpose | Default tool |
|---|---|---|
| Wide events / logs | Debugging and operations | Loki (pino/zerolog/structlog) |
| Analytics events | Product behavior and funnels | PostHog (self-host) |
| Audit events | Compliance and security history | Immutable separate store |
| Distributed traces | Cross-service request flow | Tempo (OpenTelemetry) |
| Error reporting | Exceptions and crash context | GlitchTip |
Rules:
- Do not use analytics events as audit logs.
- Do not use audit logs as debug logs.
- Do not log PII in any of them.
- Do not rely on console strings for production observability.
9. Enforcement Gates
Rules that are not checked will drift. These gates are required in CI or as PR checklist items.
| Gate | Check | Blocking? |
|---|---|---|
STANDARDS.md linked from agents.md | grep -l "STANDARDS.md" agents.md | ✅ Blocking |
AI_RULES.md references STANDARDS.md instead of duplicating stack | manual review | ✅ Blocking |
| ADR overview is up to date | pnpm run adrs && git diff --exit-code docs/adr-overview.mdx | ✅ Blocking |
| Public components have maturity records | pnpm run readiness:check | ✅ Blocking |
No raw HTML form controls (<input>, <select>, <textarea>) in component code | ESLint or grep -r '<input|<select|<textarea' src/components/ | ✅ Blocking |
No raw fetch() inside component files | ESLint or grep check | ✅ Blocking |
| No direct vendor SDK imports outside provider wrappers | dependency boundary lint | ✅ Blocking |
| No hardcoded color values (hex/rgb) in component files | grep / lint rule | ⚠️ Advisory |
No stale function Demo() in story render | pnpm run dx:audit | ✅ Blocking |
| New feature folders include tests | CI check or PR checklist | ⚠️ Advisory |
| Lint, typecheck, tests, and build pass | pnpm lint && pnpm typecheck && pnpm test && pnpm build | ✅ Blocking |
10. Migration Policy
Do not rewrite existing code just to match new standards.
When touching an existing file:
- Keep the requested change small
- Improve only the area you are touching
- Do not perform repo-wide migrations unless explicitly requested
- If a violation is nearby but out of scope, mention it in the PR summary
- Prefer incremental compliance over big-bang rewrites
11. Design System Pre-Commit Gates
Before claiming any task complete, every agent MUST run (in this order):
pnpm lint # ESLintpnpm typecheck # tsc --noEmitpnpm test # Vitest (watch for unhandled errors — they fail CI too)pnpm run readiness:check # component maturity gatepnpm build # library build + bundle:checkOptional but required when stories or visuals change:
pnpm build-storybook # confirms Storybook composes cleanlypnpm test:visual # Playwright visual regressionDo not claim “lint passes” or “tests pass” without actually running the script in the current session. If a check is skipped, say so explicitly.
12. Related Files
| File | Purpose |
|---|---|
AI_RULES.md | Quick-reference hard rules for AI agents — read this first |
DECISION_DEFAULTS.md | Quick lookup table for every default stack/pattern choice — check before inventing |
CONTRIBUTING.md | Contributor guide — conventions, pre-commit checklist, ADR rules |
.copilot/skills/README.md | Skills index — when to use, when not to, related ADRs, build priority |
| Storybook MCP | Full Storybook-first design-system agent and component guidance |
Storybook Docs/ADRs/Overview | ADR overview with one-line summaries |
docs/adrs/docs-index.json | Machine-readable index for LLM wayfinding (regenerate with pnpm run docs:index) |
docs/adr-023-observability-and-wide-events-logging.mdx | Full wide events logging standard (ADR-023) |
docs/adr-024-engineering-standards.mdx | Full engineering code standards (ADR-024) |
don't-reinvent-wheel.md | Library shortlist for behavior-heavy components |
docs/components/full-components.mdx | Interaction families and variant catalog |
docs/reports/prepare-for-usage-based.md | Full project accelerator kit plan with Quick Wins Index |
src/lib/component-maturity.ts | Component maturity registry — every public component must have an entry |
src/test/helpers.tsx | Shared test utilities: renderWithProviders, mockCurrentUser, mockPaginatedResponse, etc. |
src/components/patterns/myfreelawyer/components/virginia-citations.ts | Citation registry for legal documents |