Monday, 10 August 2026

CLAUDE.md/ SKILL.md/ rules .md files - examples

CLAUDE.md for always-on project memory, 

SKILL.md is a reusable instruction set for a specific task type

Rules files for scoped guidance

How each layer behaves at runtime

Developer opens project and starts Claude Code session
  → CLAUDE.md loads immediately — module boundaries, banned libs, git conventions

Developer asks Claude to add a new REST endpoint
  → Claude reads src/order-api/controller/OrderController.java
  → api-layer.md auto-loads (path match on src/**/controller/**/*.java)
  → Claude sees: no business logic in controllers, use records for DTOs
  → Claude recognises task type: scaffolding a REST endpoint
  → create-rest-endpoint.md skill loads on demand
  → Claude follows the skill playbook: controller + service + dto in correct layers

Developer asks Claude to add a new column to the orders table
  → Claude reads src/order-infrastructure/repository/OrderRepository.java
  → persistence.md auto-loads (path match)
  → Claude recognises task type: schema change needed
  → create-flyway-migration.md skill loads on demand
  → Claude follows versioned migration steps, creates rollback script

Developer asks Claude to review a migration SQL file
  → api-scaffolder agent not relevant here
  → migration-reviewer agent dispatched
  → agent frontmatter explicitly lists: persistence rules + create-flyway-migration skill
  → agent starts clean context but has exactly the rules it needs
  → returns focused review result to main session

One-line summary of each layer

LayerFile locationLoads whenContains
CLAUDE.mdproject rootEvery session, alwaysUniversal constraints
Rules.claude/rules/*.mdPath glob matchesPath-specific guardrails
Skills.claude/skills/*.mdTask type recognisedHow-to playbooks
Agents.claude/agents/*.mdExplicitly dispatchedCustom subagent with explicit rules + skills wired in
settings.json.claude/settings.jsonEvery sessionHooks, permissions, MCP

File structure that contains 3 of these file types

your-project/

├── CLAUDE.md                                    ← universal constraints, always loaded every session

├── .claude/
│   ├── settings.json                            ← hooks, permissions, MCP servers
│   │
│   ├── rules/                                   ← auto-loads when file path matches frontmatter glob
│   │   ├── persistence.md                       ← triggers on src/**/infrastructure/**/*.java
│   │   ├── api-layer.md                         ← triggers on src/**/controller/**/*.java
│   │   ├── domain.md                            ← triggers on src/**/domain/**/*.java
│   │   └── kafka.md                             ← triggers on src/**/messaging/**/*.java
│   │
│   ├── skills/                                  ← on-demand playbooks, loaded when task type matches
│   │   ├── create-flyway-migration.md           ← how to create a versioned DB migration
│   │   ├── create-rest-endpoint.md              ← how to scaffold controller + service + dto
│   │   ├── write-integration-test.md            ← how to set up TestContainers + @SpringBootTest
│   │   └── add-kafka-consumer.md                ← how to wire a new consumer with DLT + idempotency
│   │
│   └── agents/                                  ← custom subagents with explicit skill/rule loading
│       ├── migration-reviewer.md                ← reviews Flyway SQL, loads persistence rules + migration skill
│       └── api-scaffolder.md                    ← scaffolds endpoints, loads api-layer rules + rest-endpoint skill

└── src/
    ├── order-service/
    ├── order-domain/
    ├── order-infrastructure/
    ├── order-api/
    └── legacy-billing/                          ← deny rule in settings.json blocks agent writes here

Examples:

CLAUDE.md — root of your project

markdown
# Project: Order Management Service
# Team: Fulfillment Platform, Accenture Delivery Unit

## Build & Test Commands
- Build: `mvn clean install -DskipTests`
- Test: `mvn test -pl order-service`
- Integration test: `mvn verify -P integration-test`
- Single test: `mvn test -Dtest=OrderServiceTest`
- Lint: `mvn checkstyle:check`

## Module Structure
- `order-service/` — core domain, Spring Boot entry point
- `order-domain/` — entities, value objects, domain events
- `order-infrastructure/` — JPA repositories, Kafka producers
- `order-api/` — REST controllers, DTOs, OpenAPI spec
- `legacy-billing/` — DO NOT TOUCH. Owned by billing team.
- `shared-kernel/` — read only. Changes require architect approval.

## Framework Conventions
- Spring Boot 3.2.x — do not suggest 2.x patterns
- Java 17 — use records for DTOs, sealed classes for domain errors
- Spring Data JPA only — never write native Hibernate sessions directly
- Flyway for all schema changes — never modify schema via entity annotations alone
- MapStruct for DTO mapping — never write manual mapping code
- Lombok is banned — use Java records and explicit constructors

## Architecture Rules
- Controllers must never contain business logic
- Service layer owns transactions — annotate with @Transactional explicitly
- Repository interfaces only in order-infrastructure — never inject in controllers
- Domain events published via ApplicationEventPublisher, never direct Kafka calls from service layer
- Never expose JPA entities directly from REST endpoints — always map to DTOs

## Paths Agent Must Never Touch
- `legacy-billing/**`
- `shared-kernel/**`
- `**/application-prod.properties`
- `**/application-staging.properties`
- `.github/workflows/**`
- `db/migrations/**` — propose migration SQL, never write it directly

## Testing Standards
- Every service method needs a unit test in the same PR
- Use Mockito for mocking — never PowerMock
- Integration tests use @SpringBootTest with TestContainers
- Test class naming: `{ClassName}Test` for unit, `{ClassName}IT` for integration

## Git Conventions
- Branch naming: `feature/OMS-{ticket}`, `fix/OMS-{ticket}`
- Never commit directly to main or develop
- Commit message format: `OMS-{ticket}: {imperative verb} {what changed}`

Rules files

.claude/rules/persistence.md

markdown
---
paths:
  - "src/**/infrastructure/**/*.java"
  - "src/**/repository/**/*.java"
  - "db/migrations/**/*.sql"
---

## Persistence Layer Rules

### JPA
- All repositories extend JpaRepository or PagingAndSortingRepository
- Never use EntityManager directly unless query cannot be expressed in JPQL
- Fetch strategy is LAZY by default — justify any EAGER fetch in a comment
- Named queries go in orm.xml, not as annotations on entities
- Projections for read-only queries — never fetch full entity for a SELECT

### Transactions
- @Transactional belongs on the service layer, not repository
- ReadOnly=true on all query-only service methods
- Propagation.REQUIRES_NEW only for audit logging — document why
- Never swallow TransactionSystemException — let it propagate

### Flyway Migrations
- File naming: `V{version}__{description}.sql` e.g. V20240815_001__add_order_status_index.sql
- Never alter an existing migration file after it has run in any environment
- Every migration must have a rollback script in db/rollbacks/
- Index every foreign key column
- All VARCHAR columns need explicit length — no TEXT unless justified

.claude/rules/api-layer.md

markdown
---
paths:
  - "src/**/controller/**/*.java"
  - "src/**/api/**/*.java"
  - "src/**/dto/**/*.java"
---

## API Layer Rules

### Controllers
- Annotate with @RestController and @RequestMapping at class level
- Method-level mappings use @GetMapping/@PostMapping etc — never @RequestMapping
- Controllers inject service interfaces, never concrete implementations
- No business logic — delegate immediately to service layer
- No direct repository injection

### Request/Response
- All request bodies are Java records with @Valid
- All response bodies are Java records — never return raw entities
- Wrap paginated responses in PageResponse<T> from shared-kernel
- Use ResponseEntity<T> only when HTTP status needs to vary dynamically

### Error Handling
- All exceptions handled in GlobalExceptionHandler — never try/catch in controllers
- Return ProblemDetail (RFC 9457) for all error responses
- 400 for validation errors, 404 for not found, 409 for conflicts, 500 for unexpected
- Never expose stack traces or internal messages in response body

### OpenAPI
- Every endpoint needs @Operation and @ApiResponse annotations
- Document all possible error response codes
- Mark deprecated endpoints with @Deprecated and migration note

.claude/rules/domain.md

markdown
---
paths:
  - "src/**/domain/**/*.java"
---

## Domain Layer Rules

### Entities
- Entities are aggregate roots — enforce invariants in constructors
- No public setters — use named domain methods (e.g. confirmOrder(), cancelOrder())
- IDs are value objects — never raw Long or UUID directly on public API
- Domain events extend AbstractDomainEvent from shared-kernel

### Business Rules
- All money values use MonetaryAmount from javax.money — never BigDecimal raw
- All date/time values use Instant for storage, ZonedDateTime for display
- Status transitions validated via StatusTransitionValidator — never raw if/else chains
- Null checks via Objects.requireNonNull with descriptive message

### What Belongs Here
- Business logic and domain rules
- Domain event definitions
- Value objects and enumerations
- Domain service interfaces (implementations in infrastructure)

### What Does Not Belong Here
- No Spring annotations except @DomainService (internal)
- No JPA annotations on pure domain objects
- No DTOs — those live in order-api

.claude/rules/kafka.md

markdown
---
paths:
  - "src/**/messaging/**/*.java"
  - "src/**/event/**/*.java"
  - "src/**/producer/**/*.java"
  - "src/**/consumer/**/*.java"
---

## Messaging Rules

### Producers
- Never call KafkaTemplate directly from service layer
- All publishing goes through DomainEventPublisher interface
- Events serialized as Avro — never plain JSON for inter-service events
- Include correlationId and causationId on every event header
- Outbox pattern required for events that must survive a service crash

### Consumers
- All consumers annotated with @KafkaListener at method level
- Idempotency check before processing — log and skip duplicate messageId
- Dead letter topic configured for every consumer group
- Never throw unchecked exceptions from consumer — catch, log, send to DLT
- Consumer offset committed only after successful processing

### Naming Conventions
- Topic: `{domain}.{aggregate}.{event}` e.g. `fulfillment.order.confirmed`
- Consumer group: `{service-name}-{topic-short-name}` e.g. `order-service-order-confirmed`
- Dead letter: `{topic-name}.DLT` e.g. `fulfillment.order.confirmed.DLT`


SKILL files  : A skill is a portable Markdown file (SKILL.md file) placed in .claude/skills. The front matter identifies the skill and describes when it applies, and the body holds the steps.

.claude/skills/create-flyway-migration.md

markdown
# Skill: Create Flyway Migration

## When to use this skill
When adding a new table, column, index, or constraint to the database schema.

## Steps
1. Determine the next version number by checking db/migrations/ for the highest V number
2. Create file: `db/migrations/V{version}__{description}.sql`
3. Write forward migration SQL
4. Create matching rollback: `db/rollbacks/V{version}__{description}_rollback.sql`
5. Update the entity class if column added
6. Add @Column annotation with matching name and length
7. Run `mvn flyway:validate` to confirm migration is valid

## Naming Rules
- Version format: `V20240815_001` — date + sequence
- Description: lowercase, underscores, imperative verb first
- Example: `V20240815_001__add_shipment_tracking_column.sql`

## Never
- Never modify an existing migration file
- Never use CREATE OR REPLACE — always new versioned file
- Never skip the rollback script

How skills are loaded — this is the key difference

Rules files load automatically when a file path matches. Skills load on demand — either:

  • You reference them explicitly in CLAUDE.md: See skill: create-flyway-migration
  • A subagent lists them in its frontmatter
  • Claude Code determines the skill is relevant to the current task

So the mental model is:

  • Rules = always-on guardrails scoped to a path
  • Skills = reusable playbooks loaded when a specific task type is needed