CLAUDE.md

June 2, 2026 ยท View on GitHub

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Essential Commands

This is a pnpm + Turborepo monorepo. Use pnpm instead of npm.

Root-level convenience scripts follow the <app>:<task> pattern (e.g., webapp:dev, docs:build). When adding new apps that require dev servers or build steps, add matching <app>:<task> shortcuts to the root package.json.

Webapp

  • pnpm webapp:dev - Start webapp dev server on port 3000
  • pnpm webapp:build - Build webapp for production
  • pnpm webapp:test -- --run - Run Vitest unit tests (always use --run flag)
  • pnpm webapp:validate - Run all tests, linting, and typecheck (use before commits)
  • pnpm webapp:start - Start webapp production server locally (loads .env from monorepo root)

IMPORTANT: When running tests manually, ALWAYS use the --run flag to run tests once and exit. Without --run, Vitest runs in watch mode which consumes excessive memory. Never run multiple test processes in parallel as this can freeze the system.

Companion App (Mobile)

  • pnpm companion:dev - Start Metro dev server (connects to existing build)
  • pnpm companion:dev:clear - Start Metro with cleared cache (after env changes)
  • pnpm companion:build:ios - Build native iOS + run on Simulator
  • pnpm companion:build:ios:device - Build native iOS + run on physical iPhone
  • pnpm companion:build:android - Build native Android + run on device/emulator
  • pnpm companion:prebuild:clean - Regenerate iOS native project from Expo config
  • pnpm companion:doctor - Run react-doctor against the companion app (React Native diagnostics: deprecated modules, reanimated, FlatList perf, hook misuse)

See apps/companion/README.md for full setup guide (LAN IPs, HTTP mode, device trust).

Docs

  • pnpm docs:dev - Start docs dev server on port 5173
  • pnpm docs:build - Build docs for production
  • pnpm docs:preview - Preview docs production build on port 5174

Code Quality

  • pnpm webapp:lint - ESLint checking (webapp only)
  • pnpm turbo lint - ESLint checking (all packages)
  • pnpm --filter @shelf/webapp lint:fix - Fix ESLint issues automatically
  • pnpm turbo typecheck - TypeScript type checking (all packages)
  • pnpm run format - Prettier code formatting (root-level)
  • pnpm --filter @shelf/webapp validate - Complete pre-commit validation
  • pnpm webapp:doctor - Run react-doctor against the webapp (React health diagnostics: hook misuse, perf, a11y, architecture). Not part of validate or the pre-commit hook. It does run in CI: the ๐Ÿฉบ React Doctor GitHub Action scans changed files on every PR (matrix over webapp + companion), posts a per-app sticky comment, and fails the check on newly-introduced errors (warnings stay advisory). See companion:doctor for the React Native equivalent.

Security Review Agent (pre-commit)

A Claude-powered security reviewer runs automatically on git commit against security-sensitive diffs (routes, *.server.ts, prisma, Supabase wiring, server middleware, new dependencies). It catches the regressions hit most often โ€” cross-org IDORs, missing requirePermission gates, open redirects, missing Zod validation, audit-trail gaps โ€” before code reaches review.

  • Interactive subagent: .claude/agents/shelf-security-reviewer.md (full toolset โ€” for manual claude --agent shelf-security-reviewer ... use with permission prompts).
  • Headless subagent: .claude/agents/shelf-security-reviewer-headless.md (Skill-only โ€” what the pre-commit hook invokes; safe under bypassPermissions because there's no Bash/network channel to exfiltrate through).
  • Pre-commit wrapper: scripts/security-review-staged.sh
  • Wired into lefthook.yml at priority 5 (after typecheck)

Advisory by default โ€” findings print, the commit proceeds. Opt in to blocking with SHELF_SEC_REVIEW_BLOCK=1; skip with SHELF_SEC_REVIEW=0. Manual use: claude --agent shelf-security-reviewer "review PR #N".

๐Ÿ“– Full documentation: apps/docs/security-review-agent.md.

Database

All database commands run via the @shelf/database package (packages/database/). This package owns the Prisma schema, migrations, and client generation. The webapp does not manage database concerns directly โ€” it consumes @shelf/database as a workspace dependency.

  • pnpm db:generate - Generate Prisma client after schema changes
  • pnpm db:prepare-migration - Create new database migration
  • pnpm db:deploy-migration - Apply migrations and regenerate client
  • pnpm db:reset - Reset database (destructive!)
  • pnpm webapp:setup - Generate Prisma client + deploy migrations (for initial setup/onboarding)

Build & Production

  • pnpm turbo build - Build all packages and apps for production
  • pnpm webapp:start - Start production server locally (loads .env from monorepo root)
  • pnpm run start (inside apps/webapp/) - Used by Docker/Fly (env vars from platform)

Monorepo Structure

This is a pnpm workspaces + Turborepo monorepo. All packages are defined in pnpm-workspace.yaml and orchestrated by turbo.json.

Apps

PackagePathDescription
@shelf/webappapps/webapp/Remix web application โ€” the main product. Contains routes, components, modules (business logic), and integrations.
@shelf/companionapps/companion/Expo/React Native mobile companion app. QR/barcode scanning, asset management, audits, bookings. Uses webapp API.
@shelf/docsapps/docs/Developer documentation site (VitePress). Contains guides on local development, database triggers, architecture, etc.

Packages

PackagePathDescription
@shelf/databasepackages/database/Owns all database concerns: Prisma schema (prisma/schema.prisma), migrations (prisma/migrations/), and the createDatabaseClient() factory (src/client.ts). All db:* root scripts delegate to this package. The webapp imports from this package โ€” it does not run Prisma commands directly in production.

Tooling

PackagePathDescription
@shelf/typescript-configtooling/typescript/Shared tsconfig base configurations extended by all other packages.

How packages connect

  • Webapp โ†’ Database: The webapp depends on @shelf/database (workspace dependency). Its app/database/db.server.ts is a thin wrapper that calls createDatabaseClient() from @shelf/database. All 135+ ~/database/db.server imports in the webapp work unchanged.
  • Webapp โ†’ Prisma types: The webapp's build, typecheck, and validate scripts run prisma generate to ensure types are available. In CI, this is done via pnpm --filter @shelf/database run db:generate.
  • Vite config: The webapp's vite.config.ts includes ssr.noExternal: ["@shelf/database"] so Vite bundles it correctly, and aliases .prisma/client/index-browser for browser builds.

Architecture Overview

Shelf.nu is an asset management platform built with Remix, React, TypeScript, and PostgreSQL.

Core Technologies

  • Remix - Full-stack React framework with file-based routing
  • Prisma - Database ORM with PostgreSQL
  • Supabase - Authentication, storage, and database hosting
  • Tailwind CSS + Radix UI - Styling and UI components
  • Jotai - Atomic state management

Key Directory Structure

shelf/
โ”œโ”€โ”€ turbo.json                       # Turborepo pipeline config
โ”œโ”€โ”€ pnpm-workspace.yaml              # Workspace package definitions
โ”œโ”€โ”€ packages/
โ”‚   โ””โ”€โ”€ database/                    # @shelf/database โ€” Prisma client + types
โ”‚       โ”œโ”€โ”€ prisma/schema.prisma
โ”‚       โ”œโ”€โ”€ prisma/migrations/
โ”‚       โ””โ”€โ”€ src/client.ts            # createDatabaseClient() factory
โ”œโ”€โ”€ apps/
โ”‚   โ””โ”€โ”€ webapp/                      # @shelf/webapp โ€” Remix app
โ”‚       โ”œโ”€โ”€ app/
โ”‚       โ”‚   โ”œโ”€โ”€ routes/              # File-based routes (remix-flat-routes)
โ”‚       โ”‚   โ”œโ”€โ”€ modules/             # Business logic services
โ”‚       โ”‚   โ”œโ”€โ”€ components/          # Reusable React components
โ”‚       โ”‚   โ”œโ”€โ”€ database/db.server.ts # Thin re-export from @shelf/database
โ”‚       โ”‚   โ”œโ”€โ”€ atoms/               # Jotai state atoms
โ”‚       โ”‚   โ”œโ”€โ”€ utils/               # Utility functions
โ”‚       โ”‚   โ””โ”€โ”€ integrations/        # Third-party service integrations
โ”‚       โ””โ”€โ”€ server/                  # Hono server entry + middleware
โ””โ”€โ”€ tooling/
    โ””โ”€โ”€ typescript/                  # Shared tsconfig bases

Route Organization

  • _layout+/ - Main authenticated application routes
  • _auth+/ - Authentication and login routes
  • _welcome+/ - User onboarding flow
  • api+/ - API endpoints
  • qr+/ - QR code handling for assets

Development Patterns

State Management

  • Server State: Remix loaders/actions for data fetching and mutations
  • Client State: Jotai atoms for complex UI state
  • URL State: Search params for filters, pagination, and bookmarks
  • Optimistic UI & nProgress: When implementing optimistic UI with fetchers, add the fetcher key to the excludeFetchers array in apps/webapp/app/hooks/use-nprogress.ts so the global loading bar does not show for operations that already provide instant visual feedback.

Data Layer

  • Prisma Schema: Located in packages/database/prisma/schema.prisma (owned by @shelf/database)
  • Client Generation: Always run via @shelf/database (pnpm db:generate), never from the webapp directly
  • DB Client: @shelf/database exports createDatabaseClient() factory; the webapp's app/database/db.server.ts is a thin wrapper
  • Row Level Security (RLS): Implemented via Supabase policies
  • Full-text Search: PostgreSQL search across assets and bookings

Component Architecture

  • Modular Services: Business logic separated into apps/webapp/app/modules/
  • Reusable Components: Organized by feature/domain in apps/webapp/app/components/
  • Form Handling: Remix Form with client-side validation
  • UI Primitives: Radix UI components with Tailwind styling
  • Date Display: Always use the DateS component (apps/webapp/app/components/shared/date.tsx) for displaying dates in the UI. Do not use raw toLocaleDateString() or other custom date formatting.

Email Templates

All HTML emails must follow the design established in app/emails/stripe/audit-trial-welcome.tsx:

  • React Email components: Html, Head, Container, Text, Button, Link
  • LogoForEmail at the top of every email
  • Shared styles from app/emails/styles.ts (styles.p, styles.h2, styles.button, styles.li)
  • Personalized greeting with user's first name: Hey {firstName},
  • CTA buttons using styles.button (not bare links)
  • Info/warning boxes: yellow background #FFF8E1 + border #FFE082 for important notices
  • Both HTML and plain text exports: HTML via render(), plain text as template literal
  • Send wrapper function with try/catch + Logger.error + ShelfError
  • Closing: The Shelf Team

Button Type Prop (Required)

Every <Button> that renders as a native <button> element must have an explicit type prop. This is enforced by the local-rules/require-button-type ESLint rule.

  • Use type="submit" for buttons that submit a form
  • Use type="button" for all other buttons (modals, toggles, actions, etc.)
  • Buttons with to= (link buttons) or as="a"/as="span" do not need type
// โŒ Bad - missing type
<Button onClick={handler}>Cancel</Button>

// โœ… Good
<Button type="button" onClick={handler}>Cancel</Button>
<Button type="submit" disabled={disabled}>Save</Button>
<Button to="/home">Home</Button>  // Link button, no type needed

Why: The HTML spec defaults <button> to type="submit", which can cause accidental form submissions. Explicit types prevent this and make intent clear.

Disabled State for Form Submissions

Always use the useDisabled hook from ~/hooks/use-disabled to disable buttons during form submission. Do not use useNavigation directly to check navigation.state.

import { useDisabled } from "~/hooks/use-disabled";

// Inside component:
const disabled = useDisabled();
// For fetcher forms, pass the fetcher:
const disabled = useDisabled(fetcher);

<Button type="submit" disabled={disabled}>
  {disabled ? "Saving..." : "Save"}
</Button>

Deprecated Components

  • DropdownMenu (apps/webapp/app/components/shared/dropdown.tsx): Do not use for new features. Instead, use Popover from @radix-ui/react-popover with custom select behavior. See apps/webapp/app/components/assets/assets-index/advanced-filters/field-selector.tsx for a good example implementation.

Silencing react-doctor findings

react-doctor runs in CI on every PR for both the webapp (pnpm webapp:doctor) and the companion app (pnpm companion:doctor): warnings are advisory, but newly-introduced errors fail the PR check, so keep both clean. The guidance below applies to both apps.

Important: react-doctor does not respect // eslint-disable-next-line comments. The only way to silence a finding is to refactor the code so the pattern no longer matches. Standard ESLint disable comments still work for pnpm webapp:lint โ€” they just don't help with pnpm webapp:doctor.

Refactor strategies for common findings:

  • react/no-danger for static CSS injection โ€” use React's native <style>{cssString}</style> form (safe text child). See apps/webapp/app/components/shared/mobile-dropdown-styles.tsx for the reusable mobile-dropdown helper.
  • jsx-a11y/no-autofocus โ€” remove the autoFocus prop, then focus imperatively with ref.current?.focus() inside a useEffect when intentional modal/form focus is needed. This satisfies the rule and keeps the UX.
  • react/no-danger for scripts (e.g., <script> injecting window.env) โ€” if no refactor is possible, the finding will remain. Leave a short // why: comment above the call site so maintainers understand why it's there, and treat it as an accepted residual.

When you must leave a finding in place (e.g., SSR script injection, third-party API that only returns HTML), add a // why: comment above the code even though the finding will still appear in scans. The comment is for humans reviewing the diff later, not for the tool.

// why: <explanation>
// react-doctor flags this but refactoring would regress <X>
<script ... />

Form Validation Pattern (Required)

IMPORTANT: All forms MUST display server-side validation errors as a fallback. Client-side validation can fail or be bypassed, so server-side errors must always be shown to users.

Why This Matters:

  • Client-side validation can be bypassed (disabled JS, modified requests)
  • Zod schemas may behave differently on client vs server (e.g., date comparisons)
  • Users must always see meaningful error messages, never generic "Something went wrong"

Implementation Steps:

  1. Import required utilities:
import { useActionData } from "react-router";
import { getValidationErrors } from "~/utils/http";
import type { DataOrErrorResponse } from "~/utils/http.server";
  1. Get validation errors from action data:
// Inside your component
const actionData = useActionData<DataOrErrorResponse>();

/** This handles server side errors in case client side validation fails */
const validationErrors = getValidationErrors<typeof yourZodSchema>(
  actionData?.error
);
  1. Display server errors as fallback in each input:
<Input
  name={zo.fields.fieldName()}
  error={
    validationErrors?.fieldName?.message || zo.errors.fieldName()?.message
  }
  // ... other props
/>

Complete Example:

// Schema definition
export const myFormSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email"),
  date: z.coerce.date().min(new Date(), "Date must be in the future"),
});

// Component
export default function MyForm() {
  const zo = useZorm("MyForm", myFormSchema);

  const actionData = useActionData<DataOrErrorResponse>();
  const validationErrors = getValidationErrors<typeof myFormSchema>(
    actionData?.error
  );

  return (
    <Form method="POST">
      <Input
        name={zo.fields.name()}
        error={validationErrors?.name?.message || zo.errors.name()?.message}
        label="Name"
      />
      <Input
        name={zo.fields.email()}
        error={validationErrors?.email?.message || zo.errors.email()?.message}
        label="Email"
      />
      <Input
        type="datetime-local"
        name={zo.fields.date()}
        error={validationErrors?.date?.message || zo.errors.date()?.message}
        label="Date"
      />
      <Button type="submit">Submit</Button>
    </Form>
  );
}

Working Examples:

  • Reminder dialog: apps/webapp/app/components/asset-reminder/set-or-edit-reminder-dialog.tsx
  • Booking form: apps/webapp/app/components/booking/forms/edit-booking-form.tsx

Accessibility

All UI implementations must meet WCAG 2.1 AA as a minimum. This includes:

  • Sufficient color contrast ratios (4.5:1 for normal text, 3:1 for large text)
  • All interactive elements must be keyboard accessible
  • Form inputs must have associated labels
  • Use aria-describedby to link inputs to helper/error text
  • Meaningful alt text for images and icons
  • Focus indicators must be visible

Code Documentation (Required)

All code must include inline documentation and JSDoc comments. This applies to every new file and every new export.

File-level documentation:

  • Every file must start with a JSDoc block explaining its purpose, responsibilities, and how it fits into the broader system
  • Include @see references to related files (routes, services, components) where helpful

Function/component-level documentation:

  • Every exported function, component, and type must have a JSDoc comment
  • Describe what it does, its parameters (@param), return values (@returns), and thrown errors (@throws)
  • For React components, document the props

Inline comments:

  • Add inline comments to explain non-obvious logic, business rules, or important distinctions
  • Especially important: when a variable name could be confused (e.g., userId referring to different users in different contexts), add a clarifying comment
  • Explain "why" rather than "what" โ€” the code shows what, comments explain why

Example:

/**
 * User Note Service
 *
 * Handles CRUD operations for admin notes on user profiles.
 * Notes are workspace-scoped: a note in Workspace A is invisible in Workspace B.
 *
 * @see {@link file://./../../routes/_layout+/settings.team.users.$userId.note.tsx}
 */

/** Arguments for creating a user note */
type CreateUserNoteArgs = { ... };

/**
 * Creates a new note on a user's profile within a specific workspace.
 *
 * @param args - The note content, target user, organization, and optional author
 * @returns The created UserNote record
 * @throws {ShelfError} If the database operation fails
 */
export async function createUserNote(args: CreateUserNoteArgs) { ... }

Code Abstraction

  • When you notice duplicated code patterns across multiple files or functions, abstract them into reusable helper functions
  • Before implementing new functionality, check if similar logic already exists that can be extracted and reused
  • Keep helper functions focused on a single responsibility
  • Place shared helpers near the code that uses them, or in a shared utils file if used across multiple modules

Key Business Features

  • Asset Management: CRUD operations, QR code generation, image processing
  • Booking System: Calendar integration, conflict detection, PDF generation
  • Multi-tenancy: Organization-based data isolation
  • Authentication: Supabase Auth with SSO support

Bulk Operations & Select All Pattern

When implementing bulk operations that work across multiple pages of filtered data, follow the ALL_SELECTED_KEY pattern:

The Pattern:

  1. Component Layer - Pass current search params when "select all" is active
  2. Route/API Layer - Extract and forward currentSearchParams
  3. Service Layer - Use getAssetsWhereInput helper to build where clause from params

Key Implementation Points:

  • Use isSelectingAllItems() from apps/webapp/app/utils/list.ts to detect select all
  • Always pass currentSearchParams alongside assetIds when ALL_SELECTED_KEY is present
  • Use getAssetsWhereInput({ organizationId, currentSearchParams }) to build Prisma where clause
  • Set takeAll: true to remove pagination limits

Working Examples:

  • Export assets: apps/webapp/app/components/assets/assets-index/export-assets-button.tsx
  • Bulk delete: apps/webapp/app/routes/_layout+/assets._index.tsx (action)
  • QR download: apps/webapp/app/routes/api+/assets.get-assets-for-bulk-qr-download.ts

๐Ÿ“– Full Documentation: See docs/select-all-pattern.md for detailed implementation guide, code examples, and common pitfalls.

Testing Approach

Unit Tests (Vitest)

  • Tests co-located with source files
  • Happy DOM environment for React component testing
  • Run with pnpm webapp:test -- --run or pnpm --filter @shelf/webapp test:cov for coverage

Validation Pipeline

Always run pnpm webapp:validate before committing - this runs:

  1. Prisma type generation
  2. ESLint with auto-fix
  3. Prettier formatting
  4. TypeScript checking
  5. Unit tests

Writing & Organizing Tests

Test Philosophy

  • Write behavior-driven tests focusing on observable outcomes rather than implementation details.
  • Tests should describe what the system does, not how it does it.
  • Avoid testing internal private methods or state; instead, test public interfaces and user-visible effects.

When to Mock

  • Mock only external network calls, time-based functions, feature flags, or heavy dependencies that are impractical or slow to run in tests.
  • Avoid mocking internal business logic or utility functions to keep tests realistic and maintainable.
  • Prefer using real implementations where possible to catch integration issues early.

Mock Justification Rule

  • Every mock must be accompanied by a // why: comment explaining the reason for mocking.
  • This encourages thoughtful use of mocks and helps reviewers understand test design choices.

Organizing Mocks and Factories

  • Test files: Co-located with source files (e.g., apps/webapp/app/modules/user/service.server.test.ts)
  • Shared mocks: Place in apps/webapp/test/mocks/ directory, organized by domain (remix.tsx, database.ts)
  • Factories: Place in apps/webapp/test/factories/ directory for generating test data
  • MSW handlers: Keep in apps/webapp/mocks/ directory for API mocking

Example directory structure:

apps/webapp/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ modules/
โ”‚   โ”‚   โ””โ”€โ”€ user/
โ”‚   โ”‚       โ”œโ”€โ”€ service.server.ts
โ”‚   โ”‚       โ””โ”€โ”€ service.server.test.ts  # Co-located test
โ”œโ”€โ”€ test/
โ”‚   โ”œโ”€โ”€ mocks/
โ”‚   โ”‚   โ”œโ”€โ”€ remix.tsx          # Remix hook mocks
โ”‚   โ”‚   โ””โ”€โ”€ database.ts        # Database/Prisma mocks
โ”‚   โ””โ”€โ”€ factories/
โ”‚       โ”œโ”€โ”€ user.ts            # User factory
โ”‚       โ”œโ”€โ”€ asset.ts           # Asset factory
โ”‚       โ””โ”€โ”€ index.ts           # Export all
โ””โ”€โ”€ mocks/                      # MSW API handlers
    โ”œโ”€โ”€ handlers.ts
    โ””โ”€โ”€ index.ts

Path Aliases (Configured)

Path aliases are configured in vitest.config.ts for easy imports:

import { createUser } from "@factories"; // โ†’ apps/webapp/test/factories/index.ts
import { createRemixMocks } from "@mocks/remix"; // โ†’ apps/webapp/test/mocks/remix.tsx

Factories & Test Data

  • Use factories to generate consistent and realistic test data.
  • Factories should allow overrides for specific fields to tailor data for each test case.
  • Avoid hardcoding data within tests; use factories to keep tests clean and maintainable.

Example factory usage:

import { userFactory } from "@factories/userFactory";

const testUser = userFactory.build({ role: "admin" });

Pre-Commit Checklist

Before committing tests:

  • Ensure tests are behavior-driven and do not rely heavily on implementation details.
  • Confirm mocks have // why: comments explaining their necessity.
  • Verify tests run quickly and reliably without flaky behavior.
  • Check that test data is generated via factories or well-structured mocks.
  • Review test readability and maintainability.

Environment Configuration

The .env file lives at the monorepo root (not inside apps/webapp/). Copy .env.example to .env and fill in your values. Vite, Prisma, and all db:* commands load from this single root file.

Required Environment Variables

  • DATABASE_URL and DIRECT_URL - PostgreSQL connections
  • SUPABASE_URL and SUPABASE_ANON_PUBLIC - Supabase configuration
  • SESSION_SECRET - Session encryption key

Feature Flags

  • ENABLE_PREMIUM_FEATURES - Toggle subscription requirements
  • DISABLE_SIGNUP - Control user registration
  • SEND_ONBOARDING_EMAIL - Control onboarding emails

Important Files to Understand

  1. packages/database/prisma/schema.prisma - Complete database schema and relationships
  2. apps/webapp/app/config/shelf.config.ts - Application configuration and constants
  3. apps/webapp/app/modules/ - Core business logic services (asset, booking, user, etc.)
  4. apps/webapp/app/routes/_layout+/ - Main authenticated application routes
  5. apps/webapp/vite.config.ts - Build configuration with Remix and development settings
  6. packages/database/src/client.ts - Database client factory (shared across apps)

Development Workflow

  1. Database Changes: Modify packages/database/prisma/schema.prisma โ†’ pnpm db:prepare-migration โ†’ pnpm db:deploy-migration (runs via @shelf/database)
  2. New Features: Create in apps/webapp/app/modules/ for business logic, apps/webapp/app/routes/ for pages
  3. Component Updates: Follow existing patterns in apps/webapp/app/components/
  4. Testing: Write unit tests for utilities
    Follow the testing conventions outlined in the Writing & Organizing Tests section to ensure consistent, behavior-driven testing and minimal mocking.
  5. Pre-commit: Always run pnpm webapp:validate to ensure code quality

Git and Version control

  • NEVER stage (git add) or commit files automatically. Only stage or commit when the user explicitly asks you to do so.
  • Always use Conventional Commits spec when making commits and opening PRs: https://www.conventionalcommits.org/en/v1.0.0/
  • use descriptive commit messages that capture the full scope of the changes
  • IMPORTANT: Each line in the commit message body must be โ‰ค 100 characters
    • Wrap long lines to stay within the limit
    • This is enforced by commitlint pre-commit hook
    • Subject line can be longer, only body lines are restricted
  • dont add ๐Ÿค– Generated with [Claude Code](https://claude.ai code) & Co-Authored-By: Claude noreply@anthropic.com" because it clutters the commits
  • Include test readability and mock discipline in PR reviews. Overly mocked or verbose tests should be refactored before merge.

Rule Improvement Triggers

  • New code patterns not covered by existing rules
  • Repeated similar implementations across files
  • Common error patterns that could be prevented
  • New libraries or tools being used consistently
  • Emerging best practices in the codebase

Analysis Process:

  • Compare new code with existing rules
  • Identify patterns that should be standardized
  • Look for references to external documentation
  • Check for consistent error handling patterns
  • Monitor test patterns and coverage

Rule Updates:

  • Add New Rules When:

    • A new technology/pattern is used in 3+ files
    • Common bugs could be prevented by a rule
    • Code reviews repeatedly mention the same feedback
    • New security or performance patterns emerge
  • Modify Existing Rules When:

    • Better examples exist in the codebase
    • Additional edge cases are discovered
    • Related rules have been updated
    • Implementation details have changed
  • Example Pattern Recognition:

    // If you see repeated patterns like:
    const data = await prisma.user.findMany({
      select: { id: true, email: true },
      where: { status: "ACTIVE" },
    });
    
    // Consider adding to the files
    // - Standard select fields
    // - Common where conditions
    // - Performance optimization patterns
    
  • Rule Quality Checks:

  • Rules should be actionable and specific

  • Examples should come from actual code

  • References should be up to date

  • Patterns should be consistently enforced

Continuous Improvement:

  • Monitor code review comments
  • Track common development questions
  • Update rules after major refactors
  • Add links to relevant documentation
  • Cross-reference related rules

Rule Deprecation

  • Mark outdated patterns as deprecated
  • Remove rules that no longer apply
  • Update references to deprecated rules
  • Document migration paths for old patterns

Documentation Updates:

  • Keep examples synchronized with code

  • Update references to external docs

  • Maintain links between related rules

  • Document breaking changes

  • When you write any knowledgebase articles or documentation always provide the content in markdown