Rubric
Contents — domains, guide and mocks

Configuration management

CCDV-F 2.613 min read · checked 21 September 2026

Task statementConfiguration Management (4.1%) — CLAUDE.md files, settings.json, model version pinning, prompt versioning, and plugin dependencies

Settings precedence, highest first

the top value wins

  1. Managed settingsyour organisation; you cannot override it
  2. Command lineclaude --settings for this session
  3. Project local.claude/settings.local.json — you, this project
  4. Shared project.claude/settings.json — committed, everyone
  5. User~/.claude/settings.json — you, every project
A key set higher up overrides the same key set lower down. Knowing this order is the most directly testable thing in the task statement.

Four files, four audiences

Claude Code reads settings from four files, plus managed settings an organisation delivers. Each has a scope, and choosing correctly is most of the discipline.

FileWho it affectsWhat belongs there
~/.claude/settings.jsonYou, in every project on this machinePersonal preferences: theme, editor mode, your default model
.claude/settings.jsonEveryone working in that folderTeam permissions, hooks, plugins, project environment
.claude/settings.local.jsonYou, in this one projectPersonal overrides and experiments before you share them
Managed settingsEveryone the organisation deploys toSecurity policy and compliance requirements

Two operational details matter more than they look. The shared project file is only shared if you commit it — until then it is an ordinary file on your disk and nobody else has it. And the local file is meant to stay out of the repository: Claude Code keeps it out of your commits when it creates the file, but if you create it by hand, adding it to .gitignore is your job. The documentation's own example is the clean one: the team's committed file sets a model for everyone, and a developer who wants a different one sets it in their local file, changing only their own sessions.

A few settings never take effect from a repository file at all, and some — project allow rules, extra marketplaces, most environment values — wait until each teammate trusts the folder. Deny rules apply immediately. That asymmetry is deliberate: a committed file may loosen nothing until you trust it, but it may restrict things straight away. The precedence order and the scope rules are covered from the operator's side in 3.1; here they are a configuration-management concern.

CLAUDE.md as configuration

A CLAUDE.md file is the project's standing instructions: conventions, how the repository is laid out, the commands that matter, the things a newcomer always gets wrong. Anthropic's guidance for repository automation is to define code style, review criteria and project rules there, because Claude follows them when working in the repository — and, in the same breath, to keep the file concise, since it is read on every run.

Treat it as configuration rather than as documentation. It is committed, reviewed and diffed like code; it earns its length; and a rule that belongs to one person goes somewhere personal instead. The hierarchy of CLAUDE.md files and how they combine is 3.1's subject.

A CLAUDE.md that earns its tokens

Read on every run, mostly wastedtext

# Our project
This project was started
in 2023 by the platform
team. It is written in
TypeScript. TypeScript is
a typed superset of
JavaScript.

We care about quality
and we value clear
communication.

History: v1 was a
prototype, v2 added
billing ...

Specific and actionabletext

# Conventions
- Tests: `pnpm test`
  (not npm)
- Migrations live in
  db/migrations; never
  edit an applied one
- Public API changes
  need a changeset

# Gotchas
- `src/legacy/` is
  frozen; open a ticket
  instead of editing
The left version is a README that happens to be in the wrong file. The right version tells Claude the things it could not infer from the repository itself.

Pinning the model

A model identifier is a dependency version, and it belongs in configuration, once, not scattered across call sites. What has changed is what pinning looks like. From the 4.6 generation onward, identifiers are dateless — claude-sonnet-4-6, claude-opus-5 — and each one is itself a fixed snapshot: the documentation states plainly that such an ID is not an alias, it is the snapshot, and that Anthropic does not update the weights of an existing model ID. New versions ship as new identifiers.

Aliases in the old sense — an evergreen pointer that resolves to the newest dated snapshot — exist only for pre-4.6 models, and those are explicitly not recommended for production, because the thing they point at moves. Dated identifiers such as claude-haiku-4-5-20251001 remain pinned snapshots. Every identifier, dated or dateless, carries its own deprecation and retirement schedule, which is the maintenance half of the story (2.2).

One place that decides which model runstypescript
// config.ts — the only file that names a model
export const config = {
  // Pinned identifier; changing it is a reviewed commit, not a deploy-time guess
  modelId: process.env.CLAUDE_MODEL_ID ?? "claude-sonnet-4-6",
  promptVersion: "triage/2026-09-12",   // travels with the model in logs
  schemaVersion: 3,
} as const;

// Every call site reads config.modelId — nothing hard-codes a model name.
const resp = await client.messages.create({
  model: config.modelId,
  max_tokens: 512,
  system: prompts.triage,
  messages,
});

Versioning prompts

A prompt is the part of the system with the most influence over behaviour and, in most teams, the least version discipline. The remedy is not exotic: prompts live in the repository as files, they change on a branch with a reason in the commit message, and they are reviewed like code. Then the evaluation set can be run against the old and new versions, and “the answers got worse in September” becomes a question with an answer.

The habit that turns this from tidiness into usefulness is recording the combination. A given output was produced by a model identifier, a prompt version, a schema version and a set of tool definitions. Log all four with the result. Change one at a time when you can, so that when quality moves you know which lever moved it.

What has to be pinned together

A reproducible runlogged with every result
  • Model identifiera snapshot, in configuration
  • Prompt versiona file in the repository
  • Schema versionthe output contract
  • Tools and pluginsdefinitions and their versions
Any one of these changing can change the output. A run is reproducible only when all four are recorded, which is why they belong in the same configuration.

Plugin dependencies

Plugins can depend on other plugins, and by default a dependency tracks the latest available version — which means an upstream release can change what your plugin runs on without anybody deciding to. Version constraints fix that. Dependencies are listed in the dependencies array of .claude-plugin/plugin.json: a bare string for “whatever the marketplace provides”, or an object with name, an optional semver version range such as ~2.1.0, and an optional marketplace.

`plugin.json` with one loose and one constrained dependencyjson
{
  "name": "deploy-kit",
  "version": "3.1.0",
  "dependencies": [
    "audit-logger",
    { "name": "secrets-vault", "version": "~2.1.0" }
  ]
}

Resolution is git-tag based: releases are tagged {plugin-name}--v{version}, and Claude Code fetches the highest tag satisfying the range. When several installed plugins constrain the same dependency, the ranges are intersected and the highest version satisfying all of them wins; ranges that cannot be reconciled fail the install with a range conflict rather than silently picking one. Depending on a plugin in a different marketplace is refused unless the root marketplace opts in by listing that marketplace in allowCrossMarketplaceDependenciesOn, so one marketplace cannot quietly pull in code from a source you never reviewed.

ErrorWhat it meansWhat you do
dependency-unsatisfiedA declared dependency is missing or disabledInstall it, or enable it
range-conflictTwo plugins' ranges cannot be combinedUpdate or remove one, or ask upstream to widen its range
dependency-version-unsatisfiedThe installed version is outside the declared rangeRe-resolve by reinstalling the dependency
no-matching-tagNo release tag satisfies the rangeCheck upstream tagging, or relax the range

Two management conveniences follow from the same mechanism. A plugin whose manifest is little more than a dependencies array is a bundle — installing it installs the whole curated set, which is how a platform team ships “everything a backend engineer needs” as one install. And rolling that bundle out across an organisation is a settings job: add it to enabledPlugins in managed settings. Enabling a plugin also enables what it depends on; disabling one is refused while another enabled plugin still needs it.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Hard-coding a model identifier at each call siteName it once in configuration and have every call site read it.
Using a pre-4.6 alias in productionUse a pinned snapshot identifier, since aliases move to the newest version under you.
Committing personal preferences in the shared project fileKeep them in your user file, or in the project-local file that stays out of git.
Declaring plugin dependencies by bare name onlyAdd a semver range so an upstream release cannot change your plugin's behaviour without a decision.
Keeping prompts outside version controlStore them as files, review changes, and log the prompt version with each result.
Letting CLAUDE.md grow into a project historyKeep it short and specific — it is read on every run and costs tokens each time.

You should now be able to

  • Place a setting in the correct file for its audience and predict which value wins.
  • Explain why the shared project file only affects teammates once it is committed, and why the local file is gitignored.
  • Pin a model correctly, including what changed with dateless identifiers and where aliases still exist.
  • Version prompts and schemas alongside code, and log the combination that produced an output.
  • Declare a plugin dependency with a semver range and interpret the common dependency errors.
  • Roll a standard plugin set out to an organisation through settings rather than by hand.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    A developer adds "model" to the committed .claude/settings.json because they prefer a different model from the team default. Teammates start seeing the new model too, and one of them has a managed setting from their organisation that specifies another model again.

    What has gone wrong, and what does the teammate actually get?

    1. ANothing is wrong; the committed file is the right place for a personal preference, and managed settings lose to it.
    2. BA personal preference was put in the shared file; the teammate gets the managed value, which outranks it.
    3. CThe shared file is ignored entirely, so everyone keeps the user-level value.
    4. DThe developer should have used the command line, which is the only per-person mechanism.
    Show answer and reasoning
    1. AIncorrect. Both halves are wrong: a personal preference does not belong in the shared file, and managed settings take precedence over it.
    2. BCorrect. The committed file applies to everyone in the project, and managed settings sit above every local file, so the organisation's value wins for that teammate.
    3. CIncorrect. The shared file does apply to everyone in the project; it simply sits below managed settings and the project-local file.
    4. DIncorrect. Command-line settings work for one session, but the durable per-person places are the user file and the project-local file.
  2. Question 2

    A team's application names its model at fourteen call sites. Two of them use a pre-4.6 alias, the rest use a dateless identifier. Output quality changes noticeably one week with no release.

    Which two statements explain the situation and the fix? (Select 2.)

    1. AA pre-4.6 alias is an evergreen pointer, so those two call sites can move to a newer snapshot on their own.
    2. BThe model identifier belongs in one configuration module that every call site reads.
    3. CDateless 4.6-generation identifiers are aliases too, so all fourteen call sites are unpinned.
    4. DSetting the anthropic-version header to a newer date would have pinned the model.
    5. ELowering temperature would have prevented the change in quality.
    Show answer and reasoning
    1. ACorrect. Aliases resolve to the most recent dated version, which is exactly why the documentation advises against them in production.
    2. BCorrect. One named place makes the version a reviewed change and removes the possibility of two call sites disagreeing.
    3. CIncorrect. The documentation states that a 4.6-generation ID is not an alias; it is the snapshot, with fixed weights.
    4. DIncorrect. That header versions the API's request and response shape, not which model answers.
    5. EIncorrect. Sampling settings affect variability within a model; they do not control which model version is called.
  3. Question 3

    An internal plugin depends on another team's plugin by bare name. After an upstream release, the dependent plugin stops loading and the error says the installed version is outside its declared range.

    What is the appropriate response?

    1. ARemove the dependency declaration so the plugin loads again.
    2. BCopy the upstream plugin's files into your own so nothing external can change.
    3. CRe-resolve the dependency against current constraints, then declare a tested semver range.
    4. DDisable auto-update for every marketplace in the organisation.
    Show answer and reasoning
    1. AIncorrect. It would load, and then fail at run time when the capability it needs is absent.
    2. BIncorrect. Vendoring freezes the code but abandons updates and fixes, and duplicates the maintenance burden.
    3. CCorrect. Reinstalling resolves the dependency against all current ranges, and a declared range stops the next upstream release from moving it unannounced.
    4. DIncorrect. It stops this class of surprise by stopping all updates, including security fixes, and leaves the missing constraint unfixed.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.