Shared conventions: what goes in the repo, what stays personal
By the end you can
- Route each convention to the settings scope whose audience matches who should get it
- Predict which committed keys reach teammates immediately, which wait for folder trust, and which never apply from a repository file
- Split a working personal setup into a committed team file and a gitignored local file
- Choose between a CLAUDE.md instruction and a settings rule based on whether it must be enforced
Your setup works. Eleven levels of it: permission rules you no longer get asked about, a hook that
formats on write, a marketplace, a CLAUDE.md that has stopped you re-explaining the build three
times a week. So you copy the useful parts into .claude/settings.json, commit, and tell the team
to pull.
A week later: two people say nothing changed for them, one says a rule from the file is blocking
something they need, and one is on Claude Code on the web where half of it never arrived. You run
/status and everything is there. Nothing is broken — every one of those outcomes is the documented
behaviour of the scope you picked.
The four files, and who each one reaches
Claude Code reads settings from four files, and an organization can also deliver managed settings from the claude.ai console. Each source has an audience.
| File | Who it reaches | Use it for |
|---|---|---|
~/.claude/settings.json | You, in every project on this machine | Theme, editor mode, default model, your own permission rules |
.claude/settings.json | Everyone working in the folder that contains it | Team permissions, hooks, plugins, the environment variables the project needs |
.claude/settings.local.json | You, in this one project only | Personal overrides for one project, and testing before you share |
| Managed settings | Everyone your organization deploys it to; nothing you set overrides it, apart from a few security-sensitive exceptions | Security policy and compliance requirements |
The shared file's audience is stated as a folder, not a team:
Everyone working in the folder that contains it. In a git repository, commit it so teammates get it — Settings
Until you commit it, it is a file on your disk like any other and nobody else has it. That is the first half of this lesson's trap; the second half is that committing it is not sufficient either.
Precedence runs the other way from the reading order most people assume. Highest first: managed
settings, then command line (--settings and per-key flags, for one session), then project
local, then shared project, then user. A key at a higher level overrides the same key
anywhere below it.
Committed does not mean applied
Two things keep a key in a committed .claude/settings.json from applying for everyone who clones
it, and they fail differently.
Claude Code ignores the key in a repository file. Some keys are read only from your own or your
organization's files. The settings index marks them in its Scope column — User, local, or managed,
User or managed, Managed, or Global config, the last applying only from ~/.claude.json. None
of those apply from the shared file, apart from autoContinueAtUsageLimit, which a repository file
can still switch off while no user, --settings, or managed value sets it. That is the whole of the
exception, and it is younger than the rule it bends — the key "Requires Claude Code v2.1.234 or
later" (Settings reference), so a shared file
that names it is doing something only on builds that have it. For every other key, a Scope column
reading User or managed means the committed file is ignored, full stop, and a teammate who commits
that key to change the behaviour for everyone changes nothing on anybody's machine. The case you have
most likely reached for is not in that column at all: permissions.defaultMode has Scope Any file,
and it is two of its values that are restricted — auto and bypassPermissions do not take effect
from project or local settings. Set them in user or managed settings, or pass --permission-mode for
one session. Before v2.1.257, bypassPermissions took effect from any file — so a team file that
"used to work" is not evidence that it still does.
The key waits for each teammate to trust the folder. Until they do, they still see prompts and don't get plugins from a marketplace the committed file declares.
From a committed .claude/settings.json | When it applies |
|---|---|
permissions.deny, permissions.ask | Immediately |
permissions.allow | After that teammate trusts the folder |
permissions.additionalDirectories, extraKnownMarketplaces, most env values | After that teammate trusts the folder |
The docs give the rule behind that split rather than leaving you to infer it:
permissions.allowrules andpermissions.additionalDirectoriesentries in a project's.claude/settings.jsongrant capability, so Claude Code applies them only after you accept the workspace trust dialog for that folder. …denyandaskrules aren't affected, since they only restrict. — Configure permissions
Do not stretch "grants capability, therefore waits" into a general rule, because hooks break it. A
hook in a committed settings file, and a helper command such as apiKeyHelper, are used in both
situations where a teammate has not accepted the dialog for that exact folder: when they trusted only
a parent folder, and in a claude -p or SDK run, which never shows the dialog at all. Executable
content from a repository is not uniformly gated on trust — read the per-content table on
Configure permissions row by row.
A second asymmetry explains why your own allow rules always seemed to apply instantly. An untracked
.claude/settings.local.json is yours rather than the repository's, so Claude Code applies its
allow rules without the trust step it requires for the committed file. Get that file tracked by git
and Claude Code treats it as repository-supplied instead, holding its rules until you trust the
folder.
Cloud sessions read the clone, not your machine
A cloud session runs on a fresh clone of your repository, not on your machine. The shared
.claude/settings.json is read, because the file is part of the clone. ~/.claude/settings.json and
.claude/settings.local.json are not — both stay on your machine, and the local file is not in the
clone. Of the managed sources, only server-managed settings reach a cloud session; a
managed-settings.json file or MDM profile on your device does not.
So short of a managed policy, the docs give two routes into a cloud session: commit the key to the
repository's .claude/settings.json, or set an environment variable on the cloud environment. A
convention that lives in your user file is a convention your cloud sessions do not have, and
/config on the web opens the Claude Code section of your claude.ai settings rather than changing a
value for the session.
One placement detail bites in monorepos: Claude Code reads the shared .claude/settings.json from
the session's primary working directory, so to use a file committed at the repository root, start
Claude Code there. The local file does not follow that rule — in a git repository Claude Code reads
and writes .claude/settings.local.json at the repository root even when you start in a
subdirectory, so an approval you take in packages/api applies across the whole repository.
That second half is recent, and the docs date it:
Before v2.1.211, Claude Code kept the file in the starting directory. It still reads a file an earlier version left there alongside the root file; where both set the same key, the root's value applies, and permission rules from both files apply. — Settings
Read that second sentence twice, because it is the half that reaches you. Nothing cleaned up the
per-directory files earlier builds wrote. A session started in packages/api today reads that
subdirectory's leftover file and the root file, and permission rules from both apply — so an
approval you granted there on an older build, when Claude Code had nowhere else to put it, is still
in force every time you start a session in that directory. It will not be in the file you are
looking at. When you have a permission you cannot find a rule for, the root local file is only the
first place to look; the second is every subdirectory you have ever started a session in.
The root location has two exceptions, and they are worth knowing before you promise a colleague that
one approval covers the repository. The file stays next to .claude/settings.json instead when you
are outside a git repository, when the repository root is your home directory, on Windows, and when
the repository root or its .git or .claude entry is not owned by your user. And the paths inside
it do not anchor at the repository root: a permission rule that starts with /, or a relative
sandbox path, anchors at the session's primary working directory instead, so one stored rule covers
different files depending on where the session started.
Routing a team convention by audience
Settings rules are enforced by the client; a CLAUDE.md line is context Claude tries to follow.
Lesson 6.2 builds that distinction in full. The question here is the team version of it: for a given
convention, which audience needs it, and is prose enough for them?
The memory docs route by audience one level up, for an organization deciding what goes into a managed
policy, and it is the same split a team makes below it. Their list sends four concerns to settings —
blocking specific tools, commands or file paths with permissions.deny; sandbox isolation;
environment variables and API provider routing; login method and organization restrictions — and
three to a managed CLAUDE.md: code style and quality guidelines, data handling and compliance
reminders, and behavioural instructions for Claude
(Memory).
The test: if a teammate's session ignoring this would be a serious problem, it is not a CLAUDE.md
line. "Never commit to main" in prose is a strong hint; a deny rule is a rule. (What can be
enforced against a developer who edits the rule back out is Lesson 12.5; at the repository level,
every file here is one a teammate can change.)
The shared CLAUDE.md still matters — the docs say to check it into git so your team can contribute,
because it compounds in value over time
(Best practices). Its failure mode gets worse
precisely when a team edits it: a bloated file makes Claude ignore the instructions that matter, and
Lesson 6.6 builds that argument in full. What is new at team scale is who prunes. A file eight people
append to has no natural pruner. Somebody owns the cut, or the shared file becomes the mechanism by
which the team's real instructions stop being followed — the Lesson 6.6 result, arrived at socially.
Splitting a working setup into team and personal halves
Sort the setup you have by audience. Three destinations. Committed,
.claude/settings.json — things true for anyone working on this repository:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": ["Bash(npm run lint)", "Bash(npm run test *)"],
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}Gitignored, .claude/settings.local.json — same project, only you: your model choice, an approval
you took on a prompt, a value you are testing before proposing it. Claude Code writes this file
itself when you answer "Yes, and don't ask again", and the first time it writes it in a git
repository that does not already ignore it, it adds the path to your global git excludes. If you
created the file by hand and Claude Code has not written to it yet, add it to .gitignore
yourself — nothing has run that step.
Gitignored, CLAUDE.local.md at the project root — personal instructions rather than settings. The
docs describe it as the place for private per-project preferences that shouldn't be checked into
version control: your sandbox URLs, your preferred test data. It loads alongside CLAUDE.md and is
treated the same way, and you add it to .gitignore yourself.
Splitting is safer than it looks, because list keys such as permissions.allow combine across files
instead of one file's value replacing another's — a team allow list adds to a teammate's own rules,
which is why the shared file can be small. Four model-list keys are the exception and follow their
own rules: fallbackModel, availableModels, modelPicker (v2.1.242 or later), and
modelSettings (v2.1.251 or later). Note the direction merging cuts for permissions: a team file
cannot take a permission away by omitting it. Only a deny or ask rule does that, and both
outrank an allow from a lower file.
Confirming what actually loaded
Three instruments, each answering a different question. /status names the settings files this
session read, on its Setting sources line. claude doctor lists the entries Claude Code rejected,
with the error. /context lists the instruction files that loaded, under Memory files.
Read the caveat with /status: the line confirms which files Claude Code read, not which file
supplied each key. The one exception the docs give is the model — where project or managed settings
set it, the startup header names the file that did. For anything else, remove the competing value or
walk the precedence stack.
Lesson 5.1 made the general case: a claim and evidence are different objects. Here is the specific
instance. "I committed the settings" is a claim about a file. "The team's setup is live" is a claim
about other people's machines, and the only thing that tests it is somebody else, on a fresh clone,
running /status and then trying the thing the rules were supposed to permit. Build that into the
rollout rather than into the announcement — which is Lesson 12.3.
Checkpoint
-
You commit a
.claude/settings.jsonwith adenyrule on.envreads, anallowrule forBash(npm run test *), and anextraKnownMarketplacesentry for your team's plugins. A teammate clones the repo and starts a session without trusting the folder. Which of the three is in effect, and what do they experience for the other two?Show answerAnswer
Only the
denyrule on.envreads.denyandaskrules restrict rather than grant, so they apply immediately from a committed file;permissions.allowandextraKnownMarketplacesgrant capability, so Claude Code applies them only after that teammate accepts the workspace trust dialog for that folder.So they get a prompt every time
npm run testruns — the one thing you promised they would stop seeing — and no plugins from the marketplace the committed file declares. The trap is that all three work on your machine: you trusted this folder months ago, and/statuson their machine will confirm the file loaded, so "it works on my machine" is evidence about the wrong proposition. Have them trust the folder, then re-test. -
A colleague says the team file "isn't working" because their permission mode is still
defaultdespite the file settingpermissions.defaultModetoauto. What is wrong, and where does that value have to live instead?Show answerAnswer
Nothing is wrong with the file — the restriction is on the value, not the key.
permissions.defaultModehas ScopeAny file, so a committed file may set it; it isautoandbypassPermissionsspecifically that do not take effect from project or local settings. The tempting wrong diagnosis is "keys like this are ignored from a repository file", which is true of theUser or managedrows in the settings index and not of this one.Move the value to user settings (
~/.claude/settings.json) or managed settings, or pass--permission-modefor a single session. If someone insists the team file used to do this, note thatbypassPermissionstook effect from any file before v2.1.257 — a memory of it working is not evidence that it still does. -
You want every session on this repository on a specific model, and your own sessions on Opus. Which file gets which value, and what happens to a cloud session started on the same repository?
Show answerAnswer
The repository's model goes in the committed
.claude/settings.json; your Opus goes in.claude/settings.local.json. Not in~/.claude/settings.json— the user file is the lowest level there is, below the file your team commits, so the team's"model"would win over yours. The local file sits above the shared file, so only your sessions change. The model is also the one key/statuswill help you with: where project or managed settings set it, the startup header names the file that did.A cloud session runs on a fresh clone, so it reads the committed
.claude/settings.jsonand gets the repository's model. It reads neither~/.claude/settings.jsonnor.claude/settings.local.json— both stay on your machine, and the local file is not in the clone — so your Opus preference does not follow you there. Short of a managed policy, the two routes into a cloud session are committing the key or setting an environment variable on the cloud environment. -
Your team's
CLAUDE.mdsays "always run the migration check before touching schema files". Someone's session skipped it and shipped a broken migration. Is that a bug, and what would you change so it cannot recur?Show answerAnswer
Not a bug. Settings rules are enforced by the client; a
CLAUDE.mdline is context Claude tries to follow, and a session ignoring it is the documented behaviour of the scope you chose. The test this lesson gives is the one you skipped: if a teammate's session ignoring an instruction would be a serious problem, it is not aCLAUDE.mdline. A broken migration in production qualifies.So route it the way the memory docs route blocking specific tools, commands or file paths — a
denyoraskrule on the schema paths in the committed.claude/settings.json, with the prose kept as the explanation rather than as the control. Two caveats. At the repository level every file here is one a teammate can edit back out; enforcement against a developer is Lesson 12.5. And check who prunes that shared file: eight people appending to it is how a bloatedCLAUDE.mdstarts making Claude ignore the instructions that matter, which may be why this one was skipped in the first place.