claude-code-ultimate-guide/guide/workflows/team-ai-instructions.md
Florian BRUNIAUX 6d847d24de docs: add Profile-Based Module Assembly pattern (Section 3.5)
- Section 3.5 "Team Configuration at Scale" in ultimate-guide.md:
  profiles YAML + shared modules + skeleton + assembler script;
  59% context token reduction measured on 5-dev production team;
  includes CI drift detection, 5-step replication guide, trade-offs
- New workflow: guide/workflows/team-ai-instructions.md (6 phases,
  scaling thresholds, troubleshooting table)
- New templates: examples/team-config/ (profile-template.yaml,
  claude-skeleton.md, sync-script.ts)
- reference.yaml: 9 new entries for team_ai_instructions_*
- README: templates count 161 → 164, date Feb 19 → Feb 20
- CHANGELOG [Unreleased]: resource evaluations (AGENTS.md ETH Zürich
  4/5, Sylvain Chabaud 3/5), spec-first Task Granularity section,
  methodologies ATDD expansion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:04:29 +01:00

10 KiB

title description tags
Team AI Instructions Management Scale CLAUDE.md across a multi-developer team using Profile-Based Module Assembly
workflow
team
claude-md
configuration

Team AI Instructions Management

Manage AI instructions (CLAUDE.md, .cursorrules) across a team without fragmentation.

Pattern: Profile-Based Module Assembly — shared modules + per-dev profiles + automated assembler.

When to use: Team 5+ developers, multiple AI tools (Claude Code + Cursor/Windsurf), mixed OS. Skip if: Solo developer, homogeneous team (same tool, same OS), short project (<3 months).


The Problem: N x M x P Fragmentation

When a team grows, AI instructions fragment fast:

Factor Values Example
N developers 5-20 Alice, Bob, Charlie...
M tools 2-4 Claude Code, Cursor, Windsurf, Copilot
P operating systems 2-3 macOS, Linux, WSL

Total variants: N x M x P = 5 x 3 x 2 = 30 possible configurations.

Without a system, what happens:

Week 1: Team agrees on shared CLAUDE.md
Week 3: Alice adds TypeScript strict rules locally
Week 5: Bob copies Alice's file, removes half the rules
Week 8: New hire Charlie gets Bob's outdated copy
Week 12: 5 developers, 5 different CLAUDE.md files, nobody knows what's canonical

Root cause: CLAUDE.md is treated as a monolithic file instead of a composed configuration.


Architecture Overview

profiles/                    modules/
├── alice.yaml               ├── core-standards.md
├── bob.yaml                 ├── git-workflow.md
├── charlie.yaml             ├── typescript-rules.md
│                            ├── test-conventions.md
│                            ├── macos-paths.md
│                            ├── linux-paths.md
│                            ├── cursor-rules.md
│                            └── communication-verbose.md
│
├── skeleton/
│   └── claude-skeleton.md   ← Template with {{MODULE:name}} placeholders
│
└── sync-ai-instructions.ts  ← Reads profile → injects modules → writes output
        │
        ▼
output/
├── alice/CLAUDE.md          ← Generated (read-only)
├── bob/CLAUDE.md
└── charlie/CLAUDE.md

Flow: Profile (YAML) + Skeleton (template) + Modules (fragments) → Assembler → Generated CLAUDE.md


Phase 1: Audit Your Current CLAUDE.md

Goal: Classify every line as universal, conditional, or personal.

# Audit template

## Universal (all devs, all tools)
- Architecture: hexagonal
- Tests: must pass before PR
- Naming: kebab-case for files

## Conditional (depends on tool or OS)
- Cursor: use @filename syntax → module: cursor-rules
- macOS paths: /opt/homebrew → module: macos-paths
- Linux paths: /usr/local → module: linux-paths

## Personal (individual preference)
- Style: verbose explanations → profile preference
- Language: French comments → profile preference

Command to measure:

wc -l CLAUDE.md  # Total lines before modularization
# Tag each line with [U]niversal, [C]onditional, [P]ersonal
# Count by category to estimate module split

Typical result: 60% universal, 25% conditional, 15% personal.


Phase 2: Extract Modules

Goal: One .md file per thematic group.

Recommended structure:

modules/
├── core-standards.md         # Architecture, naming, patterns (all devs)
├── git-workflow.md           # Git conventions (all devs)
├── typescript-rules.md       # TS strict config (if TypeScript)
├── test-conventions.md       # Testing patterns (all devs)
├── macos-paths.md            # macOS-specific paths (if macOS)
├── linux-paths.md            # Linux paths (if Linux)
├── cursor-rules.md           # Cursor-specific rules (if Cursor)
└── communication-verbose.md  # Verbose explanation style (if preferred)

Module format (each module is a standalone Markdown fragment):

<!-- modules/typescript-rules.md -->
## TypeScript Rules

- Use strict mode: `"strict": true` in tsconfig
- Prefer `type` over `interface` for unions
- No `any` — use `unknown` + type guards
- Zod for runtime validation at boundaries

Guidelines:

  • Keep modules self-contained (no cross-references between modules)
  • 15-50 lines per module is the sweet spot
  • Name modules for their domain, not their audience
  • One module = one reason to change

Phase 3: Create Developer Profiles

Goal: One YAML per developer, listing their modules.

# profiles/alice.yaml
name: "Alice"
os: "macos"
tools:
  - claude-code
  - cursor
communication_style: "concise"
modules:
  core:
    - core-standards
    - git-workflow
    - typescript-rules
    - test-conventions
  conditional:
    - macos-paths          # auto-included when os: macos
    - cursor-rules         # auto-included when cursor in tools
preferences:
  language: "english"

Profile rules:

  • core modules: included for every dev (team standards)
  • conditional modules: included based on os and tools fields
  • preferences: personal settings injected into skeleton variables

Template for new team members: See profile-template.yaml


Phase 4: Write the Assembler Script

Goal: Script that reads profile, injects modules, outputs CLAUDE.md.

// sync-ai-instructions.ts (simplified ~30 lines)
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
import { parse } from 'yaml';
import { join } from 'path';

const profile = parse(readFileSync(`profiles/${process.argv[2]}.yaml`, 'utf8'));
let skeleton = readFileSync('skeleton/claude-skeleton.md', 'utf8');

// Collect modules from profile
const modules = [...profile.modules.core, ...profile.modules.conditional];

// Replace each placeholder with module content
for (const mod of modules) {
  const content = readFileSync(`modules/${mod}.md`, 'utf8');
  skeleton = skeleton.replace(`{{MODULE:${mod}}}`, content);
}

// Remove unused placeholders
skeleton = skeleton.replace(/\{\{MODULE:\w+\}\}/g, '');

// Write output
const outDir = `output/${process.argv[2]}`;
mkdirSync(outDir, { recursive: true });
writeFileSync(join(outDir, 'CLAUDE.md'), skeleton);
console.log(`Generated ${outDir}/CLAUDE.md (${modules.length} modules)`);

Run:

npx ts-node sync-ai-instructions.ts alice   # Single dev
npx ts-node sync-ai-instructions.ts --all   # Generate all profiles
npx ts-node sync-ai-instructions.ts --check # Verify no drift

Full template: sync-script.ts


Phase 5: CI Drift Detection

Goal: Catch when output files are out of sync with profiles/modules.

# .github/workflows/ai-instructions-check.yml
name: AI Instructions Drift Check
on:
  push:
    paths:
      - 'modules/**'
      - 'profiles/**'
      - 'skeleton/**'
  schedule:
    - cron: '0 9 * * 1-5'  # Weekdays at 9am

jobs:
  check-drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx ts-node sync-ai-instructions.ts --check
      - name: Fail if drift detected
        run: |
          if git diff --quiet output/; then
            echo "No drift detected"
          else
            echo "::error::AI instructions are out of sync!"
            git diff output/
            exit 1
          fi

What it detects:

  • A module was edited but the assembler wasn't re-run
  • A profile was added but no output CLAUDE.md exists
  • A dev manually edited their generated CLAUDE.md

Policy: Generated files are read-only. All changes go through profiles/modules, then re-run the assembler.


Phase 6: Onboarding New Developers

Goal: New dev gets their CLAUDE.md in under 5 minutes.

# 1. Clone repo
git clone <repo>

# 2. Copy profile template
cp examples/team-config/profile-template.yaml profiles/dave.yaml
# Edit: name, os, tools, modules

# 3. Generate
npx ts-node sync-ai-instructions.ts dave

# 4. Install
cp output/dave/CLAUDE.md .claude/CLAUDE.md

CLAUDE.md placement reminder:

  • Project-wide: project/CLAUDE.md (committed, for team conventions)
  • Personal overrides: .claude/CLAUDE.md (gitignored, for individual preferences)

Troubleshooting

Problem Cause Fix
Generated file too long Too many modules included Review profile: remove rarely-used modules
Module missing in output Placeholder typo in skeleton Check {{MODULE:name}} matches filename
CI drift alert Output not regenerated after module edit Run sync-ai-instructions.ts and commit
Dev A has rules Dev B doesn't Expected — it's the point Verify profile is correct for that dev
Stale output after merge Merge didn't trigger regeneration Run assembler post-merge (add git hook)

Scaling Thresholds

Team size Approach
1-2 devs Shared CLAUDE.md + precedence rules (Section 3.4)
3-5 devs, same tools Optional: modules only, no profiles
5+ devs or multi-tool Profile-Based Module Assembly (this workflow)
20+ devs Consider a CLAUDE.md config server + PR-based module changes

Measured Results

From a production team (5 developers, 3 tools, 2 OS):

Metric Before After
Lines per CLAUDE.md ~380 (monolithic) ~185 (assembled)
Token reduction 59% less context consumed
Modules extracted 0 12
Onboarding time "copy someone's file" 5 min (template + generate)
Drift incidents Weekly 0 (CI catches)