$npx -y skills add jezweb/claude-skills --skill fork-disciplineAudit and enforce the core/client boundary in multi-client projects. Detects where shared platform code is tangled with client-specific code, finds hardcoded client checks, config files that replace instead of merge, scattered client code, migration conflicts, and missing extensi
| 1 | # Fork Discipline |
| 2 | |
| 3 | Audit the core/client boundary in multi-client codebases. Every multi-client project should have a clean separation between shared platform code (core) and per-deployment code (client). This skill finds where that boundary is blurred and shows you how to fix it. |
| 4 | |
| 5 | ## The Principle |
| 6 | |
| 7 | ``` |
| 8 | project/ |
| 9 | src/ ← CORE: shared platform code. Never modified per client. |
| 10 | config/ ← DEFAULTS: base config, feature flags, sensible defaults. |
| 11 | clients/ |
| 12 | client-name/ ← CLIENT: everything that varies per deployment. |
| 13 | config ← overrides merged over defaults |
| 14 | content ← seed data, KB articles, templates |
| 15 | schema ← domain tables, migrations (numbered 0100+) |
| 16 | custom/ ← bespoke features (routes, pages, tools) |
| 17 | ``` |
| 18 | |
| 19 | **The fork test**: Before modifying any file, ask "is this core or client?" If you can't tell, the boundary isn't clean enough. |
| 20 | |
| 21 | ## When to Use |
| 22 | |
| 23 | - Before adding a second or third client to an existing project |
| 24 | - After a project has grown organically and the boundaries are fuzzy |
| 25 | - When you notice `if (client === 'acme')` checks creeping into shared code |
| 26 | - Before a major refactor to understand what's actually shared vs specific |
| 27 | - When onboarding a new developer who needs to understand the architecture |
| 28 | - Periodic health check on multi-client projects |
| 29 | |
| 30 | ## Modes |
| 31 | |
| 32 | | Mode | Trigger | What it produces | |
| 33 | |------|---------|-----------------| |
| 34 | | **audit** | "fork discipline", "check the boundary" | Boundary map + violation report | |
| 35 | | **document** | "write FORK.md", "document the boundary" | FORK.md file for the project | |
| 36 | | **refactor** | "clean up the fork", "enforce the boundary" | Refactoring plan + migration scripts | |
| 37 | |
| 38 | Default: **audit** |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## Audit Mode |
| 43 | |
| 44 | ### Step 1: Detect Project Type |
| 45 | |
| 46 | Determine if this is a multi-client project and what pattern it uses: |
| 47 | |
| 48 | | Signal | Pattern | |
| 49 | |--------|---------| |
| 50 | | `clients/` or `tenants/` directory | Explicit multi-client | |
| 51 | | Multiple config files with client names | Config-driven multi-client | |
| 52 | | `packages/` with shared + per-client packages | Monorepo multi-client | |
| 53 | | Environment variables like `CLIENT_NAME` or `TENANT_ID` | Runtime multi-client | |
| 54 | | Only one deployment, no client dirs | Single-client (may be heading multi-client) | |
| 55 | |
| 56 | If single-client: check if the project CLAUDE.md or codebase suggests it will become multi-client. If so, audit for readiness. If genuinely single-client forever, this skill isn't needed. |
| 57 | |
| 58 | ### Step 2: Map the Boundary |
| 59 | |
| 60 | Build a boundary map by scanning the codebase: |
| 61 | |
| 62 | ``` |
| 63 | CORE (shared by all clients): |
| 64 | src/server/ → API routes, middleware, auth |
| 65 | src/client/ → React components, hooks, pages |
| 66 | src/db/schema.ts → Shared database schema |
| 67 | migrations/0001-0050 → Core migrations |
| 68 | |
| 69 | CLIENT (per-deployment): |
| 70 | clients/acme/config.ts → Client overrides |
| 71 | clients/acme/kb/ → Knowledge base articles |
| 72 | clients/acme/seed.sql → Seed data |
| 73 | migrations/0100+ → Client schema extensions |
| 74 | |
| 75 | BLURRED (needs attention): |
| 76 | src/server/routes/acme-custom.ts → Client code in core! |
| 77 | src/config/defaults.ts line 47 → Hardcoded client domain |
| 78 | ``` |
| 79 | |
| 80 | ### Step 3: Find Violations |
| 81 | |
| 82 | Scan for these specific anti-patterns: |
| 83 | |
| 84 | #### Client Names in Core Code |
| 85 | |
| 86 | ```bash |
| 87 | # Search for hardcoded client identifiers in shared code |
| 88 | grep -rn "acme\|smith\|client_name_here" src/ --include="*.ts" --include="*.tsx" |
| 89 | |
| 90 | # Search for client-specific conditionals |
| 91 | grep -rn "if.*client.*===\|switch.*client\|case.*['\"]acme" src/ --include="*.ts" --include="*.tsx" |
| 92 | |
| 93 | # Search for environment-based client checks in shared code |
| 94 | grep -rn "CLIENT_NAME\|TENANT_ID\|process.env.*CLIENT" src/ --include="*.ts" --include="*.tsx" |
| 95 | ``` |
| 96 | |
| 97 | **Severity**: High. Every hardcoded client check in core code means the next client requires modifying shared code. |
| 98 | |
| 99 | #### Config Replacement Instead of Merge |
| 100 | |
| 101 | Check if client configs replace entire files or merge over defaults: |
| 102 | |
| 103 | ```typescript |
| 104 | // BAD — client config is a complete replacement |
| 105 | // clients/acme/config.ts |
| 106 | export default { |
| 107 | theme: { primary: '#1E40AF' }, |
| 108 | features: { emailOutbox: true }, |
| 109 | // Missing all other defaults — they're lost |
| 110 | } |
| 111 | |
| 112 | // GOOD — client config is a delta merged over defaults |
| 113 | // clients/acme/config.ts |
| 114 | export default { |