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 3000pnpm webapp:build- Build webapp for productionpnpm webapp:test -- --run- Run Vitest unit tests (always use--runflag)pnpm webapp:validate- Run all tests, linting, and typecheck (use before commits)pnpm webapp:start- Start webapp production server locally (loads.envfrom 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 Simulatorpnpm companion:build:ios:device- Build native iOS + run on physical iPhonepnpm companion:build:android- Build native Android + run on device/emulatorpnpm companion:prebuild:clean- Regenerate iOS native project from Expo configpnpm 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 5173pnpm docs:build- Build docs for productionpnpm 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 automaticallypnpm turbo typecheck- TypeScript type checking (all packages)pnpm run format- Prettier code formatting (root-level)pnpm --filter @shelf/webapp validate- Complete pre-commit validationpnpm webapp:doctor- Run react-doctor against the webapp (React health diagnostics: hook misuse, perf, a11y, architecture). Not part ofvalidateor the pre-commit hook. It does run in CI: the๐ฉบ React DoctorGitHub 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 manualclaude --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 underbypassPermissionsbecause there's no Bash/network channel to exfiltrate through). - Pre-commit wrapper:
scripts/security-review-staged.sh - Wired into
lefthook.ymlat 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 changespnpm db:prepare-migration- Create new database migrationpnpm db:deploy-migration- Apply migrations and regenerate clientpnpm 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 productionpnpm webapp:start- Start production server locally (loads.envfrom monorepo root)pnpm run start(insideapps/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
| Package | Path | Description |
|---|---|---|
@shelf/webapp | apps/webapp/ | Remix web application โ the main product. Contains routes, components, modules (business logic), and integrations. |
@shelf/companion | apps/companion/ | Expo/React Native mobile companion app. QR/barcode scanning, asset management, audits, bookings. Uses webapp API. |
@shelf/docs | apps/docs/ | Developer documentation site (VitePress). Contains guides on local development, database triggers, architecture, etc. |
Packages
| Package | Path | Description |
|---|---|---|
@shelf/database | packages/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
| Package | Path | Description |
|---|---|---|
@shelf/typescript-config | tooling/typescript/ | Shared tsconfig base configurations extended by all other packages. |
How packages connect
- Webapp โ Database: The webapp depends on
@shelf/database(workspace dependency). Itsapp/database/db.server.tsis a thin wrapper that callscreateDatabaseClient()from@shelf/database. All 135+~/database/db.serverimports in the webapp work unchanged. - Webapp โ Prisma types: The webapp's
build,typecheck, andvalidatescripts runprisma generateto ensure types are available. In CI, this is done viapnpm --filter @shelf/database run db:generate. - Vite config: The webapp's
vite.config.tsincludesssr.noExternal: ["@shelf/database"]so Vite bundles it correctly, and aliases.prisma/client/index-browserfor 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 flowapi+/- API endpointsqr+/- 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
excludeFetchersarray inapps/webapp/app/hooks/use-nprogress.tsso 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/databaseexportscreateDatabaseClient()factory; the webapp'sapp/database/db.server.tsis 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
DateScomponent (apps/webapp/app/components/shared/date.tsx) for displaying dates in the UI. Do not use rawtoLocaleDateString()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#FFE082for 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) oras="a"/as="span"do not needtype
// โ 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, usePopoverfrom@radix-ui/react-popoverwith custom select behavior. Seeapps/webapp/app/components/assets/assets-index/advanced-filters/field-selector.tsxfor 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-dangerfor static CSS injection โ use React's native<style>{cssString}</style>form (safe text child). Seeapps/webapp/app/components/shared/mobile-dropdown-styles.tsxfor the reusable mobile-dropdown helper.jsx-a11y/no-autofocusโ remove theautoFocusprop, then focus imperatively withref.current?.focus()inside auseEffectwhen intentional modal/form focus is needed. This satisfies the rule and keeps the UX.react/no-dangerfor scripts (e.g.,<script>injectingwindow.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:
- Import required utilities:
import { useActionData } from "react-router";
import { getValidationErrors } from "~/utils/http";
import type { DataOrErrorResponse } from "~/utils/http.server";
- 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
);
- 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-describedbyto 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
@seereferences 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.,
userIdreferring 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:
- Component Layer - Pass current search params when "select all" is active
- Route/API Layer - Extract and forward
currentSearchParams - Service Layer - Use
getAssetsWhereInputhelper to build where clause from params
Key Implementation Points:
- Use
isSelectingAllItems()fromapps/webapp/app/utils/list.tsto detect select all - Always pass
currentSearchParamsalongsideassetIdswhen ALL_SELECTED_KEY is present - Use
getAssetsWhereInput({ organizationId, currentSearchParams })to build Prisma where clause - Set
takeAll: trueto 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 -- --runorpnpm --filter @shelf/webapp test:covfor coverage
Validation Pipeline
Always run pnpm webapp:validate before committing - this runs:
- Prisma type generation
- ESLint with auto-fix
- Prettier formatting
- TypeScript checking
- 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_URLandDIRECT_URL- PostgreSQL connectionsSUPABASE_URLandSUPABASE_ANON_PUBLIC- Supabase configurationSESSION_SECRET- Session encryption key
Feature Flags
ENABLE_PREMIUM_FEATURES- Toggle subscription requirementsDISABLE_SIGNUP- Control user registrationSEND_ONBOARDING_EMAIL- Control onboarding emails
Important Files to Understand
packages/database/prisma/schema.prisma- Complete database schema and relationshipsapps/webapp/app/config/shelf.config.ts- Application configuration and constantsapps/webapp/app/modules/- Core business logic services (asset, booking, user, etc.)apps/webapp/app/routes/_layout+/- Main authenticated application routesapps/webapp/vite.config.ts- Build configuration with Remix and development settingspackages/database/src/client.ts- Database client factory (shared across apps)
Development Workflow
- Database Changes: Modify
packages/database/prisma/schema.prismaโpnpm db:prepare-migrationโpnpm db:deploy-migration(runs via@shelf/database) - New Features: Create in
apps/webapp/app/modules/for business logic,apps/webapp/app/routes/for pages - Component Updates: Follow existing patterns in
apps/webapp/app/components/ - 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. - Pre-commit: Always run
pnpm webapp:validateto 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