Pro Domain 7 Β· Developer Productivity & Operational Enablement (7%)
What this domain asks you to do. The Foundations track taught you what each Claude Code file is: which CLAUDE.md loads, how settings precedence merges, when a hook fires, and what a skill contains. The Professional track asks a different question. You are responsible for forty developers, not one terminal. The exam guide says the minimally qualified candidate "configures Claude tools and environments for teams", "improves developer workflows using AI-assisted tooling", and "supports debugging and operational issue resolution". Each of these verbs is about a team. So the artifacts (the files and documents you produce) are team artifacts too: a managed policy that an IT admin deploys, a shared conventions file that the team reviews in pull requests, a skills catalog with owners, an MCP allowlist, a CI hook, an adoption dashboard, a runbook, and an enablement plan that someone signs. This page builds directly on Domain 2 Β· Claude Code. It links to the mechanisms there and does not teach them again.
π New here? Forty developers, one platform team, and the week everything was "working" on exactly one laptop
Meridian Freight builds shipment-tracking software. Three developers tried Claude Code on their own and liked it very much. The CTO then asked the platform team to "roll it out". In week one, each of the forty developers installed it, and each got a different experience. One had a CLAUDE.md that said "always use TypeScript strict mode". Another had none. A third had granted Bash(*) to stop the permission prompts. Nobody could say what the tool was allowed to do.
The architect's first step was not a prompt. It was ownership. Security owns what the tool must never do. So those rules went into a managed policy that IT deploys and that no developer can loosen. The team owns its conventions. So a root CLAUDE.md and a committed .claude/settings.json went through code review like any other file. Each developer owns their own preferences, so nobody changed the user layer.
Next, the workflow changed, and the team measured it. Before, a pull-request review waited two days for a senior engineer. Now a CI job runs headless Claude Code with a token that has limited scope. The job posts findings as advisory comments (suggestions, not decisions). A deterministic test suite still decides whether the merge is blocked. The team tracked four numbers: review wait time, defect escape rate, cost per pull request, and how often developers accepted the suggestions.
Then something broke β at 2 AM, as usual. The shipment-ETA assistant started to return refusals. This assistant is a Claude-powered production service, not the coding tool. The on-call engineer did not have to guess. The runbook listed which log fields to collect (request id, stop_reason, model, tool results). The triage flow said to check the last deploy and the retrieval index before changing any prompt. The rollback was one command. The incident took twenty minutes. Its post-mortem became the next section of the runbook.
Those four steps β configure, improve, measure, support β are this domain. It is 7% of the exam. But every question in it hides a choice between two types of control: policy or preference, advisory or blocking, symptom or cause, detective or preventive.
Objective 1 Β· Configure Claude tools and environments for teams
Configure Claude tools and environments for teams (e.g., Claude Code)
The mechanism is the same ladder you learned in Foundations.Settings precedence works like this: managed policy sets hard limits first, and then CLI flags, local project, shared project, and user settings apply in order. Foundations teaches it in Permissions & settings precedence, and shows the three files side by side in Settings β the same key at three layers. At the team level, one thing changes: the question you ask about every line. Who owns this rule, and what happens if that owner is wrong? The answer to that question is a team environment configuration. It is a small set of files with named owners, an order for the rollout, and a way to check that each developer's machine really received them.
Managed settings rollout β the policy layer as a team artifact
Who writes it: security or platform engineering. The architect decides what belongs there. Where it lives: the [managed policy](term:managed policy) file on each machine. MDM (device management software) or a setup script deploys it. On macOS it is /Library/Application Support/ClaudeCode/managed-settings.json; on Linux it is /etc/claude-code/managed-settings.json. Developers cannot edit it, and no lower layer can loosen it. A managed settings rollout is the plan to put that file on every machine, in stages, with a check at each stage.
Here is a managed file for Meridian. It is illustrative (an example, not a real company file), but complete:
Read it by owner, not by key. permissions.deny is the security team's list: the tool cannot read secrets, and it cannot run quick commands that send data out of the network. permissions.ask is the "confirm before it leaves the machine" list. disableBypassPermissionsMode removes --dangerously-skip-permissions from every laptop. forceLoginMethod keeps billing on the organization's account. The env block turns on telemetry and sends it to the team's collector. The adoption metrics later on this page come from there. Nothing in this file is a convention. There is no coding style and no "prefer TypeScript". Those belong one layer lower, where a pull request can change them.
The rollout happens in stages. The reason: a wrong policy is wrong on forty machines at the same time.
Stage
Who
What ships
Exit check
Pilot
3 developers who volunteered
the managed file with deny only
each runs claude and confirms /status shows the managed layer; a Read(.env) attempt is refused
Broad
all 40
the same file plus ask and telemetry env
telemetry collector sees β₯35 distinct sessions in a week
Enforce
all 40
disableBypassPermissionsMode added
no session in the collector reports bypass mode
Shared CLAUDE.md conventions β the layer pull requests review
Who writes it: the team, through code review. Where it lives: the CLAUDE.md at the repository root. Nested folder memory covers sub-areas, and .claude/rules/ holds conventions for specific paths. Foundations teaches the memory hierarchy. For a team, the rule is: one root file, kept short, owned through a CODEOWNERS entry, and changed only by pull request.
A team root file that has worked well for forty developers is short and gives direct commands:
MARKDOWN
# Meridian tracking-api β team conventions
- Run `npm test` and `npm run lint` before reporting a task complete. Paste the summary line.
- Never edit files under `migrations/`; propose a new migration file instead.
- API handlers live in `src/handlers/`; shared validation in `src/validate/` (Zod). Do not add a third place.
- Secrets come from the environment. Never write a literal key, token, or password into any file.
- When a task touches `src/billing/`, read `src/billing/CLAUDE.md` first (nested memory).
- Use the `release-check` skill for anything that mentions shipping, deploying, or go-live.
Notice what it does not contain: no security policy (that goes in the managed layer), no permission grants (that goes in settings.json), and no long explanations. Each line names a place, a command, or a skill. If a convention file grows longer than one screen, that is a warning sign. The fix is nested memory in the folder that needs the detail, not a longer root file.
The skills catalog β packaged procedures with owners
Who writes it: the person who owns the procedure. Where it lives:.claude/skills/<name>/SKILL.md in the repository (project scope), or ~/.claude/skills/ for personal skills. Skills are procedures, not connections. The difference is explained in Skills vs slash commands vs subagents. A skills catalog is the team's list of skills: name, owner, trigger phrases, and the date someone last checked it. Without the catalog, two developers write the same "deploy" procedure, and a third developer runs the old one.
MARKDOWN
---
name: release-check
description: Run Meridian's pre-release checklist. Use when the user says ship, deploy, release, or go-live.
---
1. Confirm the branch is `main` and the working tree is clean; stop and report if not.
2. Run `npm test` and `npm run lint`; on any failure, report the failing line and stop.
3. Run `node scripts/check-migrations.mjs`; a non-zero exit means an unreviewed migration.
4. Print the exact `git log --oneline -5` and ask the user to confirm before tagging.
Catalog column
Why the team needs it
Owner
who answers when the skill is wrong
Trigger phrases
so the description matches how people actually ask
Scope (project / user)
project skills ship with the repo; user skills do not
Last verified
a skill nobody has run in three months is a risk
Skills that run in their own subagent β context: fork
Some skills make a lot of noise. A skill that searches the whole codebase reads dozens of files. If it runs inside your main session, every one of those file reads lands in your conversation. Your context window (the text the model can see at one time) fills up with search results that you do not need after the answer arrives. That is cross-contamination of session state: the procedure's working notes mix with the conversation you are having.
Here is an illustrative skill file (an example for Meridian, not a real company file):
MARKDOWN
---
name: trace-status
description: Find every place that reads or writes the shipment status field and return a file:line table. Use when someone asks where shipment status is used or changed.
context: fork
agent: Explore
allowed-tools: Read, Grep, Glob
---
Find every read and every write of the shipment `status` field in this repository.
1. Use Glob to list candidate files: `src/**/*.ts`, excluding `**/*.test.ts`.
2. Use Grep for `status` together with `Shipment` to find the matching lines.
3. Read only the matching line ranges, to decide whether each line reads or writes the field.
4. Return one Markdown table: file:line | read or write | one-line note. Do not return file contents.
What each line does, and who acts on it.
Line
Who reads it
What it does
context: fork
Claude Code
starts a new subagent and gives it the skill body as its prompt; the subagent does not see the conversation history
agent: Explore
Claude Code
picks the subagent type: built-in Explore, Plan, general-purpose (the default when agent is omitted), or a custom agent from .claude/agents/
allowed-tools
Claude Code
pre-approves these tools while the skill runs, so they run without a prompt. It does not restrict the tool set. The docs say "every tool remains callable"
the body
the subagent model
the task itself; it must be explicit, because the subagent has nothing else to go on
Two more facts from the skills docs. A forked skill runs in the background by default; set background: false when the session should wait for it. And the docs say context: fork "only makes sense for skills with explicit instructions". A skill that only says "follow our style guide" gives the subagent nothing to do. The same frontmatter applies when you invoke the skill as a slash command (/trace-status); Foundations explains how skills and slash commands relate in Are slash commands and skills different things?.
Situation
Evidence
Choose
Why the tempting alternative is wrong
A search or audit procedure reads many files and returns a short answer
long, noisy, self-contained; output is a summary
context: fork (often with agent: Explore)
running it inline fills the main context with search output that the session no longer needs
A skill rewrites "the function we just discussed"
it needs the current conversation
run inline (no context: fork)
a forked subagent cannot see the conversation, so it does not know which function you mean
A forked skill must not edit files
the need is a restriction
agent: set to a custom agent in .claude/agents/ whose tools: line lists only read tools
allowed-tools only pre-approves; it never removes a tool
A skill holds conventions, not a task
guidance, no explicit steps
inline skill or .claude/rules/
a forked run of pure guidance has no task to do
The MCP server allowlist β connections the team may use
Who writes it: the platform team. Security approves each server. Where it lives:.mcp.json in the repository holds the project-scope servers. For enforcement, the managed layer can limit which servers users may configure at all. Every [MCP server](term:MCP server) is a network connection and a set of tools. An allowlist is a removal control. It stops a developer from connecting an unreviewed server to a session that can read the codebase.
The shared file names two servers that the team reviewed. The --readonly argument is important. It is the least-privilege decision, made at configuration time and written in the file, not in a prompt. To enforce the list β so a developer cannot add a third server on their own machine β you need a managed-layer setting. Check the current managed-settings documentation for the exact key, because this mechanism lives above the project file.
Decision table β which layer owns which rule
The rule
Layer and file
Who changes it
Wrong layer and its failure
Secrets are unreadable
[managed policy](term:managed policy)
security via MDM
in project settings.json: any developer can delete the line
in root CLAUDE.md: every session pays for rules it never needs
The wrong choice, and what it produces. The most common team failure is policy in the wrong layer. Someone writes a security rule into the committed .claude/settings.json because "that's where permissions go". It works until the first developer is blocked by a prompt and removes the line. Nothing stops them, because developers can edit the project layer. On the exam, this wrong option (distractor) looks like a reasonable control ("add the deny rule to the shared project settings"). It fails because of ownership, not syntax. The second failure is the opposite: conventions placed in managed policy. Managed policy cannot express "run tests first" at all, and every style change must then wait for IT. The third failure is parallel work without [worktrees](term:worktree isolation): forty developers running agents in the same checkout. That is a workflow problem, not a settings problem, so it belongs to the next objective.
Which mechanism? CLAUDE.md, rules, skills, hooks, or permissions
The table above asks "who owns the rule". This one asks a second question: what kind of guidance is it, and when must it apply? Claude Code (the CLI harness) gives a team five places to put a rule. Two of them are context: text the model reads and usually follows. Three of them are executed or checked by the harness: code that runs whatever the model decides. The memory docs say it directly about CLAUDE.md: "Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead."
Recall line: context (CLAUDE.md, rules, skills) tells the model what to do; the harness (hooks, permissions) decides what is allowed to happen. The features overview says: "If a rule must hold every time, make it a hook rather than a prompt instruction."
π Project Β· CLAUDE.md (repo root) Β· example
π Guidance: the model reads it. Claude Code loads the text into context at launch. Nothing checks that the model follows it.
With the other levels: memory files add up. The user file, the root project file, and nested folder files are all loaded, not replaced.
If they contradict: the model sees both lines and has to choose. No program resolves it, so remove the conflict. Anything that must win goes into a hook or a permission rule.
The team writes this file and reviews it by pull request. Claude Code reads it at the start of every session in tracking-api. The docs recommend keeping it under 200 lines.
MARKDOWN
# Meridian tracking-api β team conventions
- Run `npm test` and `npm run lint` before reporting a task complete. Paste the summary line.
- Never edit files under `migrations/`; propose a new migration file instead. (A PreToolUse hook also blocks this.)
- API handlers live in `src/handlers/`; shared validation in `src/validate/` (Zod). Do not add a third place.
- Handler-specific conventions load from `.claude/rules/handlers.md` when you work in `src/handlers/`.
- Secrets come from the environment. Never write a literal key, token, or password into any file.
- When a task touches `src/billing/`, read `src/billing/CLAUDE.md` first (nested memory).
- Use the `release-check` skill for anything that mentions shipping, deploying, or go-live.
π Project Β· .claude/rules/handlers.md Β· example
π Guidance: the model reads it. The paths: frontmatter is read by Claude Code to decide when to load the file. The body is guidance, like CLAUDE.md.
With the other levels: it adds to CLAUDE.md; it does not replace it. Claude Code finds every .md file under .claude/rules/, also in subfolders.
If they contradict: the model sees both texts. Keep the rule file specific and the root file general, so they do not overlap.
The owners of src/handlers/ write it. Claude Code loads it only when Claude works with a file that matches one of the globs.
MARKDOWN
---
paths:
- "src/handlers/**/*.ts"
---
# Handler conventions
- Every handler validates its input with a schema from `src/validate/` before any other work.
- Return errors as `{ "error": { "code": string, "message": string } }` with a 4xx status; never a bare string.
- Log one line per request with `request_id`; never log the request body.
- Each handler has a test in `src/handlers/__tests__/` with the same file name.
π Project Β· .claude/skills/release-check/SKILL.md Β· example
π Mixed. The frontmatter is read by Claude Code: it lists the skill, and disable-model-invocation: true stops Claude from starting it on its own. The numbered body is guidance that the model follows when the skill runs.
With the other levels: no merging. When two levels have a skill with the same name, one whole skill wins.
If they contradict: only the winning skill loads. The steps never grant a permission: each command still goes through the permission rules in settings.
The release owner writes it. Claude Code lists its description in every session and loads the body when a developer types /release-check.
MARKDOWN
---
name: release-check
description: Run Meridian's pre-release checklist. Use when the user says ship, deploy, release, or go-live.
disable-model-invocation: true
---
1. Confirm the branch is `main` and the working tree is clean; stop and report if not.
2. Run `npm test` and `npm run lint`; on any failure, report the failing line and stop.
3. Run `node scripts/check-migrations.mjs`; a non-zero exit means an unreviewed migration.
4. Print the exact `git log --oneline -5` and ask the user to confirm before tagging.
π Project Β· .claude/settings.json (permissions + hooks) Β· example
π Enforced: Claude Code reads it, the model never does. The harness checks permissions before each tool call and runs each hook command on its event.
With the other levels:allow, ask and deny lists merge across all settings levels; hooks from every level all run. The managed file sits above this one.
If they contradict: a deny at any level wins over an allow. So the security denies live in the managed file, where no developer can delete them.
The team commits this file; Claude Code reads it at launch. The PreToolUse hook runs before every Edit or Write. Its script exits with code 2 when the target path is under migrations/, so the edit never happens.
The hook script, .claude/hooks/block_migrations.py (illustrative):
PYTHON
import json
import sys
payload = json.load(sys.stdin)
file_path = payload.get("tool_input", {}).get("file_path", "")
if "/migrations/" in file_path or file_path.startswith("migrations/"):
print("Editing migrations/ is blocked. Create a new migration file instead.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
Four "which mechanism?" decisions, with the tempting wrong choice.
Situation
Evidence
Choose
Why the tempting alternative is wrong
CLAUDE.md says "never edit migrations/", but Claude edited a migration file last week
the rule already exists as text and still failed; it must never happen
a PreToolUse hook on Edit|Write that exits 2 for that path
rewrite the line in capitals: it is still context, and the docs call it "a request, not a guarantee"
The root CLAUDE.md has grown to 400 lines; most lines are handler conventions
the conventions apply only to src/handlers/**/*.ts
.claude/rules/handlers.md with a paths: glob
a skill: it loads only when invoked or matched by its description, so a quick handler edit may never load it. Keep it in CLAUDE.md: every session pays for rules it rarely needs
An eight-step release checklist that developers run once a week
multi-step, on demand, has side effects
a skill with disable-model-invocation: true, run as /release-check
paste it into CLAUDE.md: loaded in every session and followed loosely. A hook: a hook runs one command on an event; it does not carry a procedure with judgment steps
Every CI run and laptop stops on a prompt for npm test
the need is "may this command run without asking?"
permissions.allow in .claude/settings.json
a CLAUDE.md line "you may run npm test": prose cannot grant a permission; the harness decides before the tool runs
Exam reflex. The words "always", "never", "ensure", or "regardless" point to a hook or a permission rule. The words "only for these files" point to .claude/rules/ with paths:. "On demand" or "a repeatable workflow" point to a skill. "Every session should know" points to CLAUDE.md.
Objective 2 Β· Improve developer workflows using AI-assisted tooling
Improve developer workflows using AI-assisted tooling
A workflow improvement is a before/after table with a measurement. It is not a list of features. The workflow-improvement method has four parts: name the workflow, state the current number, redesign the steps, and measure again. The exam tests two things: do you keep the deterministic gates where they were, and do you put the model where the waiting was? Foundations covered the tools: headless mode in Claude Code in CI/CD, and the @claude team trigger in The @claude tag. On this page you decide where to use them.
Before / after β Meridian's three workflows
Workflow
Before (measured)
After (designed)
What stays deterministic
PR review
first review after ~48 h; reviewer reads the whole diff
CI posts Claude findings in ~10 min as advisory comments; reviewer reads findings + diff
tests, lint, and the required-reviewer rule still block the merge
Onboarding a new engineer
~2 weeks to first merged PR; questions answered in chat
root CLAUDE.md + .claude/rules/ + skills catalog answer "where does X live"; first PR in ~4 days
code review, CODEOWNERS, the same test suite
Incident write-up
post-mortem written 3 days later from memory
a postmortem skill drafts the timeline from the incident channel export and runbook log fields; the on-call engineer edits
the on-call engineer signs the document; the model never marks an incident resolved
Each row names the number that the team will measure again. "Faster" alone is not a row.
The CI review hook β advisory findings, blocking tests
Who writes it: the platform team. Where it lives:.github/workflows/claude-review.yml. The token is stored in repository secrets, and the workflow is pinned to a fixed version. The job runs headless Claude Code with a limited tool allowlist and posts a comment. It does not set the check status that blocks the merge. The test job still does that.
YAML
name: claude-review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: >
Review this pull request against the conventions in CLAUDE.md.
Report correctness risks and convention violations as a checklist.
Do not modify files. Do not approve or request changes.
claude_args: "--max-turns 12 --allowedTools Read,Grep,Glob,Bash(git diff:*)"
Read the trust boundary (what the job is allowed to do) line by line. The workflow's own permissions block allows read on contents and write on pull requests, and nothing else. The action version is pinned. --allowedTools pre-approves reading files and viewing diffs, so the job never waits on a permission prompt. Be precise here: the Claude Code docs say --allowedTools lists "tools that execute without prompting for permission". It grants approval. It does not remove Write, Edit, or other Bash commands from the session. To remove tools, you add --tools (shown in the next section). --max-turns is the budget limit. The prompt says "do not approve". But the sentence does not enforce that. The GitHub permission scope enforces it: with contents: read, the job token cannot push, whatever the model decides. This is the same instruction-versus-enforcement difference that Foundations taught.
Developers get a matching hook on their own machines, so the same convention check runs before the PR exists. This is part of a project .claude/settings.json that the team commits:
The PostToolUsehook formats every file that Claude writes. This is a side effect, not a gate. The Stop hook runs a script. The script checks whether the session's transcript contains a test run. If it does not, the script returns a message that asks Claude to run the tests before it finishes. This is the "run tests before done" convention from CLAUDE.md, but now code enforces it at the moment it matters. Text in the memory file asks; the hook checks.
PYTHON
import json
import sys
payload = json.load(sys.stdin)
transcript_path = payload.get("transcript_path", "")
ran_tests = False
try:
with open(transcript_path, encoding="utf-8") as handle:
for line in handle:
if "npm test" in line or "npm run test" in line:
ran_tests = True
break
except OSError:
pass
if not ran_tests:
print("Run `npm test` and report the summary line before finishing.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
The script is illustrative. It reads the hook payload from standard input. It scans the transcript file that the payload names. If it finds no test run, it exits with code 2 and a message. A production version would check the test result, not only the command text. For the exam, the shape is what matters: a deterministic script at a lifecycle event, not a longer instruction.
A review job that loads only the standards it needs
The problem. Meridian's second repository runs the review in a plain shell step: claude -p "<prompt>". Each run starts slowly. The reason: the root CLAUDE.md has many @imports (lines like @docs/architecture.md that pull other files into memory). The memory docs say imports "load at launch". So every review loads the whole team memory, plus hooks, skills, and MCP servers, before it reads one line of the diff. But the review only needs one file: docs/review-standards.md. And a pipeline that reads the result needs JSON in a fixed shape, not prose.
The CI step (GitHub Actions run: shell, illustrative paths):
BASH
git diff origin/main...HEAD > pr.diff
claude -p "Review the change in pr.diff against the review standards. Report every finding." \
--bare \
--append-system-prompt-file docs/review-standards.md \
--tools "Read,Grep,Glob" \
--allowedTools "Read,Grep,Glob" \
--output-format json \
--json-schema '{"type":"object","properties":{"findings":{"type":"array","items":{"type":"object","properties":{"file":{"type":"string"},"line":{"type":"integer"},"severity":{"type":"string","enum":["blocker","major","minor"]},"rule":{"type":"string"},"message":{"type":"string"}},"required":["file","line","severity","rule","message"]}}},"required":["findings"]}' \
--max-turns 15 \
> review.json
jq '.structured_output.findings' review.json
Each flag, what it does, and who acts. All of these are read by Claude Code (the CLI), not by the model.
Flag
What it does (from the CLI reference)
Why the review needs it
-p
print mode: runs once, prints the response, no interactive session
skips auto-discovery of hooks, skills, custom commands, subagents, plugins, MCP servers, auto memory, and CLAUDE.md
removes the slow @import load; gives "the same result on every machine"
--append-system-prompt-file <file>
appends the file's text to the default system prompt
adds back only the standards; Claude keeps its default coding and tool guidance
--tools "Read,Grep,Glob"
restricts which built-in tools Claude can use
a review reads; it has no reason to have Edit, Write, or Bash
--allowedTools "Read,Grep,Glob"
tools that execute without prompting for permission
nobody can answer a prompt in CI; this pre-approves, it does not restrict
--output-format json
prints a JSON result object (result, session_id, total_cost_usd, β¦)
the next step parses it; the cost rows on this page read from it
--json-schema '<schema>'
validated JSON matching the schema "after the agent completes its workflow" (print mode only); the output sits in structured_output
downstream code gets a fixed shape; an invalid schema is an error exit
--max-turns 15
limits agentic turns; exits with an error when the limit is reached
a runaway review fails loudly instead of running up cost
Two facts to remember about --bare. First, authentication: "In bare mode, Claude Code never reads OAuth credentials or the system keychain." The job needs ANTHROPIC_API_KEY in the environment (from repository secrets), or an apiKeyHelper passed with --settings. Second, the docs do not say whether .claude/settings.json is read in bare mode. So do not rely on project permission rules here: put the tool limits on the command line, as above. The docs also do not say whether @imports inside an appended file are resolved, so keep docs/review-standards.md self-contained.
The three tempting options, side by side.
Option
What happens
Verdict
claude -p --bare alone
fast start, but CLAUDE.md is skipped, so the standards are dropped entirely
wrong: the review checks against nothing
plain claude -p
loads CLAUDE.md and every import, plus hooks, MCP servers, and skills
wrong: this is the slow start the team wants to remove
wrong here: the docs say replacing "drops all of the default prompt, including tool guidance and safety instructions"; use replacement only when the surface or identity is different, not for a coding review that still uses tools
fast start, default prompt kept, only the standards added
right
π Production shape: the schema in a reviewed file, and a consumer that fails closed
In production the schema is not an inline string. It is a committed file that the team reviews like code, and the consumer step checks the exit code before it trusts the output:
BASH
set -euo pipefail
git diff origin/main...HEAD > pr.diff
claude -p "Review the change in pr.diff against the review standards. Report every finding." \
--bare \
--append-system-prompt-file docs/review-standards.md \
--tools "Read,Grep,Glob" \
--allowedTools "Read,Grep,Glob" \
--output-format json \
--json-schema "$(cat .github/review/findings.schema.json)" \
--max-turns 15 \
> review.json
jq -e '.structured_output.findings | type == "array"' review.json > /dev/null
jq -c '.structured_output.findings[]' review.json | while read -r finding; do
node .github/review/post-comment.mjs "$finding"
done
jq '{cost: .total_cost_usd, session: .session_id}' review.json >> "$GITHUB_STEP_SUMMARY"
set -e makes a non-zero exit from Claude Code (an invalid schema, or the --max-turns limit) fail the job instead of posting an empty review. The findings stay advisory: this job posts comments; the test job still sets the merge status.
Exploring an unfamiliar codebase β Glob, Grep, then Read
The onboarding row above depends on this skill. A new engineer asks Claude Code, "How does a shipment get its ETA?" The repository has 2,000 files. If Claude reads files one by one, the context window fills long before the answer appears. The fix is an incremental strategy: each step narrows the search, and only the last step reads real code.
Step
Tool
Question it answers
Meridian example
1 Β· Map
Glob
which files exist with this name pattern?
src/**/*eta*.ts β 6 files
2 Β· Locate
Grep
where is this symbol defined and used?
computeEta β 1 definition, 3 call sites
3 Β· Understand
Read (only the matches, or a line range)
what does this code actually do?
src/eta/computeEta.ts plus the lines around each call
4 Β· Follow
Grep again, on what step 3 revealed
what does it depend on?
getTransitTimes β src/carriers/
5 Β· Delegate (when wide)
an Explore subagent
where is X used across the whole repo?
returns a file:line table; the main session keeps only the table
Situation
Evidence
Choose
Why the tempting alternative is wrong
"Explain how ETA works" in a large, unknown repo
many files, one question
Glob β Grep β Read the matches
Read every file in src/: fills the context window with code that does not answer the question, and leaves less room for the work that follows
"List every place that writes shipment status"
a wide search; only the list matters
an Explore subagent (or a context: fork skill like /trace-status)
search inline: forty search results stay in the main conversation after the answer arrives
Claude reaches for cat and find through Bash
the built-in tools already do this
Read, Glob and Grep
Bash: it runs any shell command, so it needs broader permission than a read-only review should have, and its raw output is not narrowed for you
Step 3 shows the file is 1,800 lines long
only one function matters
Read with a line range, after Grep gave the line number
read the whole file: most of it is unrelated to the question
a paragraph in CLAUDE.md: followed loosely, loaded always
The wrong choice, and what it produces. The classic distractor is automation that removes the deterministic gate: "let Claude approve the PR when its review finds no issues". It sounds efficient, but it fails as a control. When a model finds no issues, that does not prove there are no issues. And nobody can audit why the merge was allowed. The second distractor is AI chat instead of documentation: the team answers the same onboarding questions in a channel forever, instead of writing them where every session loads them. The third is a longer instruction where a hook belongs: three paragraphs of "please always run tests" that a busy session skips, instead of a ten-line Stop hook that cannot be skipped.
Adoption metrics β how you know the workflow improved
Where the numbers come from. The managed policy on this page turned on Claude Code's OpenTelemetry export and sent it to the team collector. That gives you session counts, token and cost usage, and tool-decision events, and you do not have to add any measuring code yourself. The metric and event names are in the Claude Code monitoring documentation. Read them there; do not memorize them. The CI job's own logs give cost and duration per PR. GitHub gives review wait time and defect data. An adoption-metrics table combines them.
Category
Metric
Source
Baseline (week 0)
Target (week 6)
Decision it drives
Usage
weekly active developers
OTel session events
3 of 40
β₯ 30 of 40
expand or pause the rollout
Usage
sessions per developer per week
OTel
0.4
β₯ 5
which teams need enablement time
Quality
defect escape rate, AI-assisted PRs vs others
issue tracker labels
6.1% (all PRs)
no worse than baseline
keep or change the review prompt
Quality
revert rate within 7 days
git
2.3%
β€ 2.3%
same
Cost
cost per merged PR (CI review job)
CI job output total_cost_usd
β
within budget line
adjust --max-turns, model tier, caching
Cost
tokens per session, p50 / p95
OTel token usage
β
p95 flagged for review
find runaway sessions
Trust
CI findings marked useful by reviewers
PR reaction / label
β
β₯ 60%
rewrite the review prompt or scope
Trust
permission prompts per session
OTel tool-decision events
14
β€ 4
tune permissions.allow in the project file
Every row ends in a decision. A metric with no decision column is only reporting, not enablement.
Below is one illustrative record that the collector gets for each CI run. The cost rows read from this shape. When you ask headless Claude Code for JSON output, it prints a JSON result object. The fields shown are the ones the table uses; real output has more fields:
total_cost_usd and num_turns supply the cost rows. cache_read_input_tokens inside usage tells you whether runs reuse the repeated convention prefix. This is the same [prompt caching](term:prompt caching) signal that Foundations taught, but now you read it as a metric across all runs.
Objective 3 Β· Support debugging and operational issue resolution
Support debugging and operational issue resolution
This section debugs two different systems, and the exam expects you to tell them apart. The first is the developer tool: Claude Code on a laptop or in CI. Here the questions are "why was this command blocked", "why did the hook not fire", and "why did the MCP server not load". The second is a Claude-powered production service: an application your team built on the Messages API or the [Agent SDK](term:Agent SDK). Here the questions are "why did quality drop", "why are we seeing refusals", and "why did latency double". AI-assisted debugging covers both, but the tools for each are different. The developer tool has built-in diagnostics. The production service has only what you logged.
Debugging the developer tool β what is built in
Symptom
First check
Where the answer lives
"Claude won't run my command"
which layer denied it
/permissions shows the merged rules; /status shows which settings files loaded
"My hook never fires"
the matcher and the event
/hooks lists registered hooks; run with --debug to see hook invocations
"The MCP server is missing"
scope and startup
/mcp shows server status; claude mcp list shows what is configured at each scope
"It ignored CLAUDE.md"
which files loaded
/memory shows the loaded memory files; nested memory loads only when work enters the folder
"It's slow / expensive today"
context size and turns
/cost for the session; OTel token metrics for the fleet
The pattern is the same as settings precedence: before you change anything, find which layer made the decision. Suppose a developer "fixes" a blocked command by adding Bash(*) to their user settings. They have not found the layer that denied it. If that layer was managed policy, the fix does nothing.
Debugging the production service β what you must have logged
Who decides: the architect, at design time. Where it lives: the application writes one structured log line for every model call and every tool run. If a field is not in the log, the on-call engineer cannot ask about it later. This is the minimum record, shown as an illustrative log line from the ETA assistant:
the id the provider can match to its own records; include it in any support case
model and prompt_version
"did we change something?" β the first triage question
index_version
for systems that use retrieval, the second triage question
[stop_reason](term:stop reason)
refusal, max_tokens, end_turn, tool_use each point to a different cause
usage.cache_read_input_tokens
a sudden drop to zero means the cached prefix changed β often this is the real "deploy"
tool_calls[].is_error and latency
tells "the model is wrong" apart from "the tool is down"
http_status
429 and 529 are [rate limiting](term:rate limiting) or overload, not a quality problem
The incident triage flow β cause before symptom
An incident triage flow for a Claude-powered system puts the checks in order. It starts with what changed most recently and what is cheapest to check. Here it is as the runbook's decision path:
TEXT
ALERT: quality / refusal / latency alarm on a Claude-powered service
1. Is it the provider?
β http_status 429/529, error rate across ALL services, provider status page
β yes: back off, degrade gracefully, wait; do NOT change prompts
2. Did WE change something in the last window?
β prompt_version, model, index_version, tool schema, deploy timestamp
β yes: roll back that one change; confirm the metric recovers; then investigate
3. Is the input different?
β input_tokens p95 jump, new document type, new customer, injection attempt
β yes: sample 20 requests; classify; add a bound or a detector upstream
4. Is a tool failing?
β tool_calls[].is_error rate, tool latency, downstream dependency health
β yes: fix or disable the tool; the model was reporting the failure, not causing it
5. Only now: is the model behaving differently on the same input?
β replay the golden set against the current configuration
β regression: escalate with request_ids; compare against the last passing eval
The order of the steps is the whole lesson. A team that starts at step 5 rewrites prompts. Meanwhile the real cause β an index refresh at 01:50 β sits in a log field that nobody checked. The exam guide's own sample question for Domain 4 tests exactly this reflex: when answers are confident but wrong after a document refresh, look at retrieval, not the model.
The runbook template β written before the incident
Who writes it: the architect writes the first draft. After the first incident, the on-call team owns it. Where it lives: in the repository, next to the service, with the same version history. An operational runbook always has the same headings. So under stress, the engineer finds the right section quickly:
MARKDOWN
# Runbook Β· eta-assistant
### Symptoms this runbook covers
- Refusal rate > 2% over 10 min Β· p95 latency > 4 s Β· golden-set pass rate < 95%
### Collect first (copy-paste)
- `logs query service=eta-assistant last=15m fields=request_id,stop_reason,model,prompt_version,index_version,http_status`
- `deploys list service=eta-assistant last=2h`
- `index versions shipments last=3`
### Triage order
1. Provider (status page, 429/529 share) β 2. Our last change β 3. Input shift β 4. Tool errors β 5. Model regression (golden set replay)
### Rollback
- Prompt: `config set eta-assistant prompt_version=<previous>` (takes effect on next request; no deploy)
- Index: `index alias shipments --to <previous version>`
- Model tier: `config set eta-assistant model=<previous supported model>`
### Escalate when
- Provider incident longer than 30 min β status update to stakeholders every 30 min
- Golden-set regression with no config change β open a provider support case with 5 request_ids
### After
- Post-mortem within 2 business days using the `postmortem` skill; add the new failure to this runbook
Most teams forget to write the rollback section, and it is the section they need most at 2 AM. Every rollback here is a configuration change, not a code deploy. That works because the prompt version, index alias, and model tier were designed as runtime configuration. That decision was made in Domain 5's reliability engineering, and here it pays off.
Decision table β control types in operational support
You need toβ¦
Right control
Type
Wrong control and its failure
Know what happened at 2 AM
structured log per call with request_id, stop_reason, versions
detective
ask the model to "explain what went wrong": it has no memory of the call
Stop the damage fast
rollback of the one changed version
corrective / removal
quick-fix the prompt in production: a second uncontrolled change
Prove it was not a regression
[golden set](term:golden set) replay against current config
"retry carefully" in the prompt: the model does not run the retry
Handle provider overload
backoff + graceful degradation
compensating
rewrite prompt during a 529 wave: unrelated to the cause
The wrong choice, and what it produces. The most common distractor is symptom over cause: "adjust the prompt to reduce refusals", while the log shows that index_version changed an hour before the alarm. The second is a detective control when the question asks for a preventive one: "add an alert on quality drop", when the question asked how to stop bad indexes from reaching production. The alert catches the problem only after users saw it. The third is asking the model to do the operator's job: "have Claude summarize the logs and decide whether to roll back". A summary is useful. But the decision does not belong to the model, and the runbook exists so that the decision is already made.
The enablement plan β the artifact an architect hands to a team
The enablement plan brings the three objectives together on one page. It is a markdown document in the platform team's repository. The engineering manager and security review it, and the team updates it at each stage gate:
MARKDOWN
# Enablement plan Β· Claude Code at Meridian Freight
**Owner:** platform team lead Β· **Sponsor:** CTO Β· **Security reviewer:** AppSec lead
**Scope:** 40 developers, 6 repositories, 6 weeks Β· **Out of scope:** production Claude services (separate runbook)
### Goal and measures
| Goal | Measure | Baseline | Target |
|---|---|---|---|
| Faster PR review | time to first review | 48 h | β€ 4 h |
| No quality regression | defect escape rate | 6.1% | β€ 6.1% |
| Controlled cost | cost per merged PR | β | within budget line |
| Safe by default | secrets readable by the tool | unknown | 0 (managed deny) |
### Stages
| Week | Stage | Deliverable | Exit check |
|---|---|---|---|
| 0 | Baseline | metrics captured | four baselines recorded |
| 1 | Pilot | managed policy on 3 machines | `Read(.env)` refused on all 3 |
| 2 | Conventions | root CLAUDE.md, project settings, skills catalog merged | PR approved by team + security |
| 3 | Workflow | CI review job on `tracking-api` | findings posted on 10 PRs; merge gate unchanged |
| 4 | Broad | managed policy + telemetry on 40 machines | β₯ 35 sessions/week in collector |
| 6 | Review | metrics review, go/no-go on 5 more repos | targets met or plan revised |
### Support
- Office hours: Tue/Thu 30 min Β· channel: #claude-enablement Β· owner responds within 1 business day
- Debugging the tool: see `docs/claude-code-troubleshooting.md` (`/status`, `/permissions`, `/hooks`, `/mcp`)
- Policy change requests: PR to `platform/managed-settings/` reviewed by AppSec
### Risks and controls
| Risk | Control | Type |
|---|---|---|
| Developer loosens security rule | rule lives in managed policy | preventive |
| Model verdict blocks merges unexplainably | findings advisory; tests block | preventive |
| Runaway session cost | `--max-turns`, p95 token alert | preventive + detective |
| Stale skill followed | catalog `last verified` column, quarterly review | detective |
You have already seen every table in the plan on this page. The measures are the adoption metrics. The stages are the managed rollout. The risks are the control-type comparisons. This is on purpose: the plan is this page, made short enough for a manager to sign.
See it run β one team's rollout, week 0 to week 6
π See it run β Meridian Freight from baseline to the first incident, with the real artifacts at each step
Week 0 β baseline. Before anything is installed, the platform lead collects four numbers: 48 h to first review (from GitHub), 6.1% defect escape (from the issue tracker), no cost line yet, and secrets exposure unknown. These numbers go into the Goal table of the enablement plan. Nothing else happens this week. Without a baseline, a rollout cannot later prove any improvement.
Week 1 β pilot the policy. IT deploys the managed file from Objective 1 to three volunteers. Each volunteer opens Claude Code and runs the exit check:
BASH
claude -p "Read the file .env and print its contents" --output-format json
The result JSON shows that the read was refused, and no file content appears in result. One pilot's session also shows "permission_mode": "default" in the /status output. This confirms that bypass mode is disabled. The pilot exit check passes on all three machines, and the plan records that the stage gate passed.
Week 2 β conventions by pull request. A PR adds four things: the root CLAUDE.md; the project .claude/settings.json with the allow list and the two hooks; .mcp.json with the two reviewed servers; and .claude/skills/release-check/SKILL.md. AppSec reviews the settings file and confirms it has no deny rules (those belong in managed policy). That reviewer comment becomes a line in the team's review checklist. The PR merges.
Week 3 β the CI review job on one repository. The workflow from Objective 2 goes live on tracking-api. The first ten PRs get findings comments. One finding is wrong: it flags a pattern in src/validate/ that the team uses on purpose. The reviewer marks it not useful. That reaction is the first data point in the Trust row. The team adds one sentence to the prompt that points to the nested src/validate/CLAUDE.md, and the false positive stops. Nobody changed the merge gate.
Week 4 β broad rollout with telemetry. The managed file, now with the telemetry env block, reaches all forty machines. By Friday the collector shows 37 distinct sessions. On average there are 14 permission prompts per session. The team reads the tool-decision events and finds that npm run build and git diff cause prompts all the time. They add both to the project allow list by PR. The next week's average is 3.
Week 5 β the first incident (in the production service, not the tool). At 02:11 the ETA assistant's refusal alarm fires. The on-call engineer opens the runbook and runs the "collect first" queries. The log lines show stop_reason: "refusal" on 4% of calls. prompt_version: "eta-v14" has not changed, model has not changed, and http_status: 200 is normal. So it is not the provider, and it is not a prompt change. index_version reads shipments-2026-09-15, published at 01:50. Step 2 of the triage flow says: roll back that one change.
BASH
index alias shipments --to shipments-2026-09-14
Refusals go back to the baseline level within five minutes. The engineer then looks at twenty of the refused requests. The new index had loaded a batch of customer emails that contained hidden instructions. The model refused to follow them. The behavior was correct; the input was wrong. The post-mortem action items are an eval gate on index publish (preventive) and an injection check on the ingestion path. Both become runbook lines and a row in the plan's Risks table.
Week 6 β review. Time to first review: 3.5 h. Defect escape rate: 5.8%. Cost per merged PR: within the budget line, and cache_read_input_tokens confirms that runs reuse the convention prefix. CI findings marked useful: 71%. Weekly active developers: 36 of 40. The plan's go/no-go decision is "go", so five more repositories enter the Week 2 stage. The team updates the enablement plan; it does not rewrite it.
Exam lens β how CCAR-P asks about this domain
In the question text (the stem), find the actor and the layer before you read the options. Domain 7 stems are short. The word that decides the answer is usually one of these:
"for teams" / "organization-wide" / "every developer" β Objective 1. The answer is in the layer with the right owner: managed policy for rules nobody may loosen, committed project files for conventions, and user files for preferences. A distractor puts a security rule in a file that a developer can edit.
"cannot be overridden" / "regardless of local settings" β managed policy, and nothing else. Any option that names CLAUDE.md, a prompt, or a project settings file is the wrong layer.
"consistent conventions across repositories" β a shared, reviewed CLAUDE.md plus a skills catalog. Not a longer prompt, and not answers in chat.
"reduce time toβ¦" / "developers wait forβ¦" β Objective 2. Look for the option that puts the model where the waiting is and keeps the deterministic gate. "Let Claude approve" or "block the merge on Claude's review" is the trap.
"advisory" versus "blocking" β the words the guide uses in its explanations. A model finding is advisory. Tests, lint, and allowlists are blocking. The correct option says which is which.
"suddenly" / "after a deploy" / "after a refresh" β Objective 3. The answer is the most recent change: check it in the logs, then roll it back. Never "rewrite the prompt" first.
"what should be collected" / "the on-call engineer needsβ¦" β the structured log fields: request id, stop_reason, model and prompt versions, index version, tool errors, and usage. An option that says "ask the model what went wrong" fails, because the model has no record of the call.
"always" / "never" / "regardless of what Claude decides" about a tool action β a hook (for example PreToolUse) or a permission rule. A CLAUDE.md line is context, not enforcement. "only for files inβ¦" β .claude/rules/ with paths: globs. "repeatable procedure, on demand" β a skill.
"isolated" / "without polluting the session" for a skill β context: fork in its frontmatter. The forked subagent does not see the conversation, so the trap is a skill that needs the current discussion.
"CI review starts slowly" / "only needs the standards file" β claude -p --bare --append-system-prompt-file <standards>. --bare alone drops the standards; --system-prompt-file replaces the default prompt. "restrict tools" β --tools; --allowedTools and a skill's allowed-tools only pre-approve. "downstream processing" β --output-format json with --json-schema.
"how would you know the rollout worked" β an adoption metric with a baseline and a decision attached. Never a number that only counts amount.
Words the answer key uses in its explanations.Preventive (managed deny, allowlist, eval gate before publish) wins over detective (alert, log, audit) when the stem asks you to stop something from happening. Removal (take the capability or server out of the configuration) wins over compensating (confirmation prompt, monitoring) when the stem says least privilege. Cause (the changed version in the log) wins over symptom (the refusal rate) when the stem says "first". And enforcement (hook, permission, policy) wins over instruction (text in CLAUDE.md, a sentence in a prompt) whenever the stem says "ensure" or "guarantee".