πŸŽ“ Claude Architect KB CCAR-F Β· CCAR-P study guide

Domain 2 Β· Claude Code Configuration & Workflows (20%)

This one tests the full configuration surface of Claude Code once you stop treating it as your personal assistant and start treating it as a team platform: memory files, skills, slash commands, hooks, settings, permissions, MCP wiring, headless/CI usage, and how it all relates to the Agent SDK.

πŸ“– New here? From "my terminal assistant" to "the team's platform"

Solo, Claude Code just works β€” you chat, it codes. The problems this domain solves appear the day a second person joins: they don't know the build commands you taught it, their generated code isn't formatted your way, and they just let it run a command you'd never allow.

The solution is configuration-as-code, in git: a CLAUDE.md so the repo itself teaches every session your conventions; slash commands so nobody re-types the same 20-line prompt; skills so procedures ("how we do releases") live once; hooks so formatting and safety are guaranteed by code, not requested by prose; permissions so CI can run tests but never push. Claude Code stops being a personal assistant and becomes a team platform with policy.

Why the SDK matters: once you trust that platform, you'll want the same engine inside your product (a support bot, a pipeline worker). The Agent SDK is Claude Code's engine as a library β€” same loop, tools, hooks, permissions β€” headless, embeddable. Terminal user β†’ team platform β†’ product engine is one continuous ladder, and this domain is its middle rung.

So what for the exam: most questions here are "which mechanism?" β€” memory vs skill vs slash command vs hook vs subagent vs setting. The discriminator table below answers all of them.

At a glance

Surface File / mechanism Scope
Project memory CLAUDE.md at repo root (+ nested per-directory) shared with the team via git
User memory ~/.claude/CLAUDE.md personal, all projects
Skills .claude/skills/<name>/SKILL.md (project) Β· ~/.claude/skills/ (user) on-demand expertise, model-chosen Β· harness-run, or /name
Slash commands .claude/commands/<name>.md reusable prompt templates, user-invoked
Subagents .claude/agents/<name>.md scoped workers with own prompt/tools
Hooks settings.json hooks (PreToolUse, PostToolUse, Stop, …) deterministic shell interception
Settings & permissions .claude/settings.json (shared) Β· settings.local.json (personal) Β· ~/.claude/settings.json (user) allow/deny tools, env, model
MCP servers .mcp.json (project) Β· user/local config external tools/resources
Headless claude -p "…" + --output-format json/stream-json CI/CD, scripts β€” deployed examples on Deployment & Operations

CLAUDE.md β€” the memory hierarchy

🧭 Your seat at the table: Claude Code ships with the harness pre-built β€” so your architect's job moves up a level: writing the standing stage. CLAUDE.md is you setting context and rules once so every future session inherits them; skills are you packaging expertise; hooks are you making rules deterministic; settings are you drawing the permission boundary. Domain 2 is the domain where "configuring a tool" and "architecting a system" turn out to be the same activity.

flowchart TD
  E[Enterprise managed policy] --> U[~/.claude/CLAUDE.md β€” user]
  U --> P[repo/CLAUDE.md β€” project, checked in]
  P --> N[subdir/CLAUDE.md β€” nested, loaded on demand]
  N --> C[Session context]

At the very top sits the Enterprise managed policy β€” a managed settings file an organization's IT pushes down to every machine. It loads automatically, and nothing a user, project, or CLI setting does can override it (the walkthrough below shows it in action).

🎯 Scenario 2 (team code generation β€” one of the six scenarios) loves precedence questions: enterprise policy > project settings > user settings for permissions; for memory, project CLAUDE.md governs the repo while ~/.claude/CLAUDE.md follows the person. Shared standards β†’ project file in git; personal preferences β†’ user file.

πŸ” See it run β€” four memory layers firing in one session at a fintech monorepo

Entry point: an engineer opens Claude Code in payments-monorepo/ and asks it to add a retry to the charge client.

What actually loaded at session start, top of the diagram down:

  1. [Enterprise managed policy] β€” IT ships one rule to every laptop: "Never send code to external URLs; curl to non-allowlisted hosts is denied." The engineer never sees it load, can't override it.
  2. [~/.claude/CLAUDE.md β€” user] β€” the engineer's personal file: "Answer concisely. I prefer vitest over jest." Follows them to every repo.
  3. [repo/CLAUDE.md β€” project] β€” checked into git by the team: "pnpm, never npm. All money amounts are integer cents. Run pnpm test:unit before declaring done." Governs everyone in this repo.
  4. [subdir CLAUDE.md β€” nested] β€” nothing yet. The moment Claude edits a file under packages/charge-client/, that package's CLAUDE.md ("this package must stay dependency-free") loads on demand β€” it wasn't burning context until the work went there.

Now watch precedence resolve a conflict: the user file says "concise", the project file says "run tests before done" β€” no conflict, both apply. But when the model proposes npm install retry, the project rule (more specific scope) beats any personal habit β†’ pnpm. And when it tries to curl an npm advisory page, the enterprise layer denies it regardless of what any lower file says β€” that's the one layer no local file outranks.

The exam angle: shared standards β†’ project file (in git, whole team); personal taste β†’ user file; org policy β†’ enterprise layer; and nested files are the context-budget trick β€” deep-tree knowledge that loads only when work enters that subtree.

🧭 Every layer here is a clickable real file below β€” the layers as real files.

Skills vs slash commands vs subagents β€” the discriminator

These three mechanisms β€” and the exact lines between them β€” are current as of mid-2026 β€” check current docs, because Claude Code's configuration surface keeps evolving under your feet.

Skill Slash command Subagent
Chosen by (the decision) the model, when your request matches the skill’s description β€” or you, via /name the user types /name the model delegates, or the user asks
Run by (the execution) Claude Code β€” loads SKILL.md into context, executes any bundled scripts Claude Code β€” expands the template Claude Code β€” spawns a fresh context window
Body is instructions + optional scripts/resources (SKILL.md + folder) a single prompt template (supports $ARGUMENTS) a system prompt + tool policy running in its own context window
Best for reusable expertise & procedures ("how we do X") frequent parametrized prompts isolated heavy work (review, search) that shouldn't pollute the main context

🧭 Decode the official vocabulary: docs and exam say skills are β€œmodel-invoked” β€” that means the model chooses (it reads each skill’s description and decides one is relevant, exactly the way it picks a tool from tools[]), never that the model executes. The model has no hands here either: it selects, and Claude Code β€” the harness in your terminal β€” performs the invocation. Same division of labor as the tool-use handshake. Hooks complete the spectrum: nobody chooses at runtime β€” the harness fires them on events, deterministically.

πŸͺ€ Trap: "the team keeps pasting the same 20-line release-checklist prompt" β†’ slash command. "The team wants Claude to know the deployment procedure whenever deploys come up" β†’ skill. "Code review burns the main session's context" β†’ subagent.

Skill frontmatter β€” the three options the exam names

SKILL.md files carry YAML frontmatter β€” the config block the harness parses, above the body the model reads (drill the term for the full anatomy). Three fields are tested by name:

Frontmatter What it does Exam trigger phrase
context: fork runs the skill in an isolated sub-agent context; picking WHICH agent is a second key on its own line β€” agent: general-purpose under context: fork (YAML: one key: value per line). The name resolves against the agent registry: built-ins like general-purpose, YOUR definitions in .claude/agents/*.md (frontmatter config + body-as-system-prompt), and plugin agents β€” where agent names come from, so verbose output never pollutes the main conversation. The other value is no value: omit context and the body loads inline into the current conversation β€” the default "the skill's output floods the session"
allowed-tools restricts which tools the skill may use while it runs (e.g., read-only β€” no Write/Bash) "the skill should never be able to modify files"
argument-hint prompts the developer for required parameters when they invoke the skill without arguments "developers keep invoking /deploy without saying which environment"

Project skills live in .claude/skills/ (shared via git); personal variants go in ~/.claude/skills/ under a different name so they don't shadow the team's version.

Skill or agent? β€” packaging a procedure like β€œgo live”

The discriminator above separates the mechanisms; this is the question you actually face: my team has a stage-it / ship-it / go-live procedure β€” skill or agent? The answer is a composition, not a choice:

Concern Owner
The procedure β€” steps, house rules, β€œpaste only the final URLs back” the skill (SKILL.md body β€” portable, git-shared, model-chosen by its description)
The mechanics β€” the exact commands, orderings, guards a script the skill wraps (scripts/ship.sh) β€” deterministic, testable without any model
The isolation β€” build noise never touches your session context: fork on the skill
The executor β€” which persona/tool policy runs it agent: on the skill (a built-in, or your own .claude/agents/ definition with narrower tools)
MARKDOWN
---
name: go-live
description: Use when asked to ship, deploy to production, or "go live".
context: fork                 # the WHOLE procedure runs in a disposable context…
agent: general-purpose        # …executed by this agent (or a custom deploy agent with only Bash+Read)
allowed-tools: Bash, Read
---
1. Run `scripts/ship.sh "<message>"` and wait for it to finish.
2. Verify the live URL returns 200.
3. Report back ONLY: commit shas, deploy URL, and any failure verbatim.

Saying β€œok, go live” now means: the model matches the description β†’ Claude Code forks the agent β†’ the checklist and all build noise live and die in that context β†’ your session receives three lines. This repo now carries exactly this skill β€” kb:.claude/skills/go-live/SKILL.md β€” say β€œgo live” in a session here and the ship runs forked. The anti-pattern is putting the procedure inside a deploy-agent's system prompt instead: it works via delegation, but the knowledge is locked in the executor β€” invisible to the main loop, unshareable with other agents, and drifting from the team's skill the day either is edited.

🧭 The same knowledge/executor split you already know: CLAUDE.md and skills carry what to do; agents carry who does it, where, with which tools. Compose, never duplicate.

.claude/rules/ β€” path-scoped conventions with glob frontmatter

When do rules load? .claude/rules/*.md is part of the MEMORY family: plain topic files load at session start, exactly like CLAUDE.md content β€” they are organization, not laziness. Adding paths: frontmatter is what makes a file conditional: it surfaces only when the files being worked on match the glob. So: split for tidiness β†’ always loaded; split with paths: β†’ loaded when relevant.

🧭 β€œKeep these standing rules” β€” where do they land? When you tell a session β€œfrom now on, always X”, that is a rule (memory), not a skill: the assistant writes it into ~/.claude/CLAUDE.md (global, every project) or the repo's CLAUDE.md / .claude/rules/ (project-wide), and the HARNESS loads it every session β€” nothing chooses it, it is simply always in context. A skill is the opposite contract: an on-demand procedure the model CHOOSES by description when relevant. Constraint you must never forget β†’ rule. Procedure you sometimes run β†’ skill.

A monolithic CLAUDE.md loads everything into every session. Two mechanisms shrink that:

The discriminator the exam tests (this is a sample-question pattern): conventions tied to a file type spread across the whole tree (test files everywhere, React components in many folders) β†’ glob-pattern rules, because directory-level CLAUDE.md files are directory-bound and can't follow a pattern. Conventions tied to one subtree β†’ a nested CLAUDE.md works. Relying on Claude to "infer which section applies" from one big file β†’ the wrong answer, every time it's offered.

Watch the merge happen β€” layered config, git-diff style

Two families, two different merge semantics β€” this is the visual that makes precedence stick. Left: each layer's file (weakest first). Right: the running effective config, every line badged by the layer that won; overridden values struck through.

flowchart TB
  subgraph SETTINGS [settings β€” per-key OVERRIDE, strongest wins]
    U1[user settings] --> M1{merge by key}
    P1[project settings] --> M1
    L1[settings.local.json] --> M1
    MP1[managed policy] -->|always wins| M1
    M1 --> E1[ONE effective value per key]
  end
  subgraph MEMORY [memory β€” UNION, all load together]
    E2[enterprise CLAUDE.md] --> M2{concatenate}
    U2[user CLAUDE.md] --> M2
    P2[project CLAUDE.md + rules/] --> M2
    N2[nested CLAUDE.md] -->|specificity wins at conflicts| M2
    M2 --> EF2[ALL lines in context]
  end

Settings β€” per-key override. Watch model change hands three times, permissions.deny arrive untouchable, and the allow list accumulate:

JSON
{"mode":"override","layers":[
 {"name":"user","file":"~/.claude/settings.json","entries":[
   {"k":"model","v":"claude-sonnet-5"},{"k":"permissions.allow[git]","v":"Bash(git status)"},{"k":"hooks.PreToolUse","v":"guard.sh"}],
  "note":"yours, every project β€” the weakest layer"},
 {"name":"project","file":"<repo>/.claude/settings.json","entries":[
   {"k":"model","v":"claude-opus-5"},{"k":"permissions.allow[test]","v":"Bash(npm run test:*)"}],
  "note":"committed β€” every teammate inherits"},
 {"name":"local","file":"<repo>/.claude/settings.local.json","entries":[
   {"k":"model","v":"claude-haiku-4-5"}],
  "note":"personal, gitignored β€” beats both below it"},
 {"name":"managed","file":"/Library/…/managed-settings.json","entries":[
   {"k":"permissions.deny[env]","v":"Read(.env*)"},{"k":"disableBypassPermissionsMode","v":"disable"}],
  "note":"IT's layer β€” nothing below can loosen it"}]}

Memory β€” union. Nothing is overridden; everything loads, and only an explicit conflict is resolved (most specific wins):

JSON
{"mode":"union","layers":[
 {"name":"enterprise","file":"/Library/…/ClaudeCode/CLAUDE.md","entries":[
   {"k":"org","v":"never paste customer PII"}],
  "note":"MDM-deployed, read-only"},
 {"name":"user","file":"~/.claude/CLAUDE.md","entries":[
   {"k":"style","v":"prefer uv for Python"},{"k":"ui","v":"zen-light theme by default"}],
  "note":"travels with YOU"},
 {"name":"project","file":"<repo>/CLAUDE.md","entries":[
   {"k":"tests","v":"npm run test:unit (never plain npm test)"},{"k":"style","v":"THIS repo uses poetry","conflicts":false}],
  "note":"travels with the repo β€” note style now has TWO lines loaded"},
 {"name":"nested","file":"<repo>/billing/CLAUDE.md","entries":[
   {"k":"billing","v":"ledger writes via postEntry() only"}],
  "note":"joins when working under billing/ β€” deepest wins where lines disagree"}]}

⚠️ That enterprise line β€” β€œnever paste customer PII” β€” is advisory, like all memory: the rule and an accidental paste reach the model in the same context, so it cannot prevent exposure, only shape behavior after it. What prevents is deterministic and sits BEFORE the call: the PII boundary.

🧭 The exam discriminator hiding in these two pictures: settings produce ONE value per key (ask β€œwho wins?”) while memory produces ALL the lines (ask β€œwhat's loaded β€” and which line is more specific?”). Skills and slash commands follow the memory family's shape β€” project and user versions coexist under different names; nothing overrides.

Beyond the standard mechanisms β€” the project-map pattern (a house invention)

Everything above CONDITIONS what loads: nested CLAUDE.md (by subtree), paths: rules (by file pattern). This pattern inverts the question: instead of conditionally loading more knowledge, always load a tiny index β€” a ~60-line file with one mermaid diagram of the project's spine plus a | Need | Location | lookup table β€” injected into the system prompt by the project launcher at session start.

Mechanism What loads When What it buys
one big CLAUDE.md all knowledge always simplicity; worst token bill
topic files .claude/rules/*.md all knowledge, organized session start maintainability
paths: rules a topic's knowledge when touched files match tokens spent only where they apply
nested CLAUDE.md a subtree's knowledge working under that dir locality
project map (house) an index, not knowledge session start, ~1–2K tokens replaces the search: β€œwhere does X live / how does it flow” answered with ZERO tool calls

The context math that makes it a real pattern and not a convenience: without the map, every β€œwhere is the deploy script?” costs a grep-read round trip β€” 2–6 tool turns, each returning output that lands in context and stays there. Ten questions β‰ˆ thousands of tokens of one-shot search debris. The map is ~1.5K tokens, loaded once, answers all ten, and keeps exploration read-only. It also survives compaction better than search results do β€” an index compresses; transcripts of find output don't.

🧭 Say it in one line: rules load what the session should KNOW; the map loads what the session can FIND β€” pushed knowledge vs a pushed index that makes every later pull precise. It composes with, not replaces, the standard ladder β€” and pairs with a refresh skill so the map is regenerated whenever the structure drifts (a stale index is worse than none).

Plan mode vs direct execution β€” and the Explore subagent

Two execution postures, one decision rule:

🎯 "Restructure the monolith β€” where do you start?" β†’ plan mode, explore before changing (not "start editing and let boundaries emerge"). "Single bug fix with a stack trace?" β†’ direct execution. "Discovery output is eating the context window?" β†’ Explore subagent.

Hooks β€” deterministic control (the harness inside Claude Code)

Hooks are just shell commands that fire on lifecycle events, and they can do one of three things: observe, block (exit code 2, on supported events like PreToolUse), or run side effects (say, auto-format right after a write). The key difference from instructions: these are guaranteed by code, not politely requested in prose. Timing is everything here β€” a PreToolUse hook runs before the action and can veto it outright, but a PostToolUse hook runs after the thing already happened. It can format or test the file that was just written, but it can't un-write it.

Event Fires Typical architect use
PreToolUse before a tool runs (matcher on tool name) block writes to protected paths; require approvals; lint the command
PostToolUse after a tool succeeds auto-format written files; run tests; log
UserPromptSubmit when the user submits inject context; validate
Stop / SubagentStop when Claude (or a subagent) finishes enforce completion criteria; notify
PreCompact / SessionStart around compaction / start preserve state; load context

Exit code 2 from a PreToolUse hook blocks the action and feeds stderr back to Claude β€” that's the enforcement loop.

JSON
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command",
                  "command": "python3 .claude/hooks/guard_bash.py" }]
    }],
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{ "type": "command", "command": "npx prettier --write \"$FILE\"" }]
    }]
  }
}

🎯 "How do you guarantee generated code is formatted / a dangerous command never runs?" β€” hooks (deterministic), never "add it to CLAUDE.md" (advisory). This distinction is repeatedly tested.

Permissions & settings precedence

JSON
{
  "permissions": {
    "allow": ["Bash(npm run test:*)"],
    "deny": ["Read(~/.zshrc)", "Read(.env*)"]
  }
}

The layers as real files β€” annotated, clickable

πŸ’‘ Everything above describes layers; this section lets you open them. The </> chips go to the claude-cookbooks repo (a live specimen of the hierarchy β€” it really ships nested memory and a .claude/ toolbox); locally they also open this machine's actual config in IntelliJ.

Memory (CLAUDE.md) β€” the full ladder, most specific wins

Enterprise memory β€” org-wide, MDM-deployed, read-only to users (static example β€” a personal machine won't have one, and that's checkable: ls "/Library/Application Support/ClaudeCode/")

MARKDOWN
# Acme Corp β€” engineering-wide Claude instructions   <!-- IT deploys this file; devs can't edit it -->
- Never paste customer PII into prompts; reference records by internal ID only.
- All generated code follows the Acme secure-coding checklist (wiki/SEC-101).
- Outbound network calls in examples must use the corporate proxy hostnames.
OS Enterprise files live at
macOS /Library/Application Support/ClaudeCode/ (CLAUDE.md, managed-settings.json)
Linux /etc/claude-code/
Windows C:\ProgramData\ClaudeCode\

~/.claude/CLAUDE.md β€” user memory Β· personal Β· every project you open

MARKDOWN
# My global instructions            <!-- travels with YOU, not the repo -->
- Prefer uv for Python; never install a duplicate runtime.
- All web UIs default to the zen-light theme.

<repo>/CLAUDE.md β€” project memory Β· committed Β· every teammate's session

MARKDOWN
# acme-billing-agent                <!-- travels with the REPO -->
- Run tests with `npm run test:unit` (never plain `npm test` β€” it hits staging).
- Money amounts are integer cents everywhere. No floats. Ever.

<repo>/billing/CLAUDE.md β€” nested memory Β· loaded when working under billing/

MARKDOWN
# billing/ conventions              <!-- deepest file wins for this subtree -->
- Ledger writes go through `postEntry()` β€” direct INSERTs break replayability.

Specimens in the wild: cookbook:CLAUDE.md β€” root project memory Β· cookbook:skills/CLAUDE.md β€” nested memory for one subtree Β· cookbook:managed_agents/slack/CLAUDE.md β€” per-subproject memory, five of them in one repo.

Settings β€” the same key at three layers, and who wins

JSONC
// /Library/Application Support/ClaudeCode/managed-settings.json β€” MANAGED POLICY (static example)
// Deployed by IT via MDM. WINS OVER EVERYTHING below β€” users cannot loosen it, only tighten.
{
  "permissions": {
    "deny": ["Read(.env*)", "Read(**/*.pem)",        // secrets unreadable org-wide
             "Bash(curl:*)", "Bash(wget:*)"],        // no ad-hoc exfil channels
    "ask":  ["Bash(git push:*)"]
  },
  "forceLoginMethod": "console",                     // org API billing β€” no personal claude.ai logins
  "forceLoginOrgUUID": "3f1a…-org-uuid",             // and only THIS org
  "disableBypassPermissionsMode": "disable",         // --dangerously-skip-permissions is dead here
  "env": { "HTTPS_PROXY": "http://proxy.acme.internal:8443" }
}
BASH
# CLI flags β€” one rung below managed policy, above every settings file (per invocation)
claude -p "run the release checks" \
  --model claude-opus-5 \
  --allowedTools "Bash(npm run test:*)"   # beats settings.json files for THIS run only
JSONC
// ~/.claude/settings.json β€” USER: yours, every project
{
  "model": "claude-sonnet-5",                    // ← your personal default…
  "permissions": { "allow": ["Bash(git status)"] },
  "hooks": {                                      // deterministic control β€” fires regardless of prompts
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "./guard.sh" }]
    }]
  }
}
JSONC
// <repo>/.claude/settings.json β€” PROJECT: committed, shared with every teammate
{
  "model": "claude-opus-5",                      // …overridden here for this repo…
  "permissions": { "allow": ["Bash(npm run test:*)"] }
}
JSONC
// <repo>/.claude/settings.local.json β€” LOCAL: personal, gitignored, wins over both
{
  "model": "claude-haiku-4-5"                    // …and THIS one wins (managed policy & CLI flags aside)
}

Chain, strongest first: managed policy β†’ CLI flags β†’ settings.local.json β†’ project settings.json β†’ user settings.json β€” the table above, now with faces.

πŸͺ€ Specimen with drift β€” read critically: cookbook:skills/.claude/settings.json is a real in-the-wild settings file whose shape is legacy/nonstandard β€” contextFiles and projectInfo are not Claude Code settings keys, and its hooks lack the matcher/hooks nesting shown above. Perfect exam practice: spot what a validator would reject. The rest of the .claude/ toolbox there is canonical: cookbook:.claude/commands/review-pr.md β€” a slash command Β· cookbook:.claude/agents/code-reviewer.md β€” a subagent definition Β· cookbook:.claude/skills/cookbook-audit/SKILL.md β€” a skill.

Claude Code in CI/CD (scenario 5 β€” CI/CD automation β€” territory)

Claude Agent SDK (scenario 1 & 4 β€” embedded agents & developer tooling β€” territory)

The Claude Agent SDK (which grew out of the Claude Code SDK) hands you that same agent harness as something you can call from code (TypeScript/Python): sessions, tools (Read/Write/Bash/Grep/Glob/WebFetch…), MCP connections, hooks, permissions, subagents β€” all of it, for building your own agents outside the terminal.

Drill-down: context hygiene features (`/compact`, `/clear`, `/cost`)
  • /compact summarizes the conversation to reclaim context (accept before long sessions die); auto-compaction warns as the window fills.
  • /clear resets the session; memory files persist β€” that's the point of putting durable knowledge in CLAUDE.md, not chat.
  • /cost shows token usage; architect habit: watch it in long agentic sessions.
  • Extended thinking on demand β€” colloquially triggered with phrases like "think hard" (as of mid-2026 β€” check current docs for the supported control) β€” for complex planning steps.
Drill-down: model selection inside Claude Code

/model or settings pin the model; the architecture answer mirrors model tiering (matching task difficulty to model capability and cost, from Domain 1): frontier model (Opus-class) for planning/architecture, faster models (Sonnet/Haiku-class) for bulk edits β€” and in CI, pin an exact model version for reproducibility.

Practice this domain

🎯 Done with Domain 2? Prove it: Claude Code (Q9–Q14) β€” commit to an answer before revealing. Locally, your picks are captured with timestamps and scored per section, so weak spots surface on the practice page's comfort tiles.