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

Samples β€” CLAUDE.md, Skills, Hooks, MCP, Settings

Nothing makes config click like a real file you can copy. Every sample here is minimal but production-shaped β€” adapt freely. They map to Domain 2 (Claude Code) and Domain 4 (MCP), and each one ends with why it scores on the exam.

A team CLAUDE.md (project memory)

MARKDOWN
# CLAUDE.md β€” acme-billing-service
## Commands
- Build: `npm run build` Β· Test: `npm test` (Jest) Β· Single test: `npx jest path/to.test.ts`
- Local stack: `./start.sh` (API :8080 + Postgres via docker compose)

## Architecture
- Hexagonal: `src/domain` (pure logic, no IO) Β· `src/adapters` (DB, HTTP) Β· `src/api` (routes).
- All money is integer cents. Never floats. Timestamps are UTC ISO-8601.

## Conventions
- TypeScript strict; no `any` without a `// justified:` comment.
- Errors: never swallow; wrap with context and rethrow typed errors from `src/errors.ts`.
- Every new endpoint needs: zod schema, unit test, and an entry in `docs/api.md`.

## Etiquette
- Never commit directly to `main`; branch + PR.
- Do not modify `migrations/` files that already shipped β€” create new migrations.

Why it scores: short, imperative, durable knowledge only β€” commands, architecture facts, hard rules. No task instructions, no essays. (The specifics here β€” Hexagonal layering, zod schemas, typed errors β€” are illustrative for readers who know TypeScript service architecture; the transferable lesson is what kind of facts belong in CLAUDE.md, not this stack.)

Specimens: cookbook:CLAUDE.md β€” root project memory Β· cookbook:skills/CLAUDE.md β€” nested, one subtree.

A skill (.claude/skills/release-check/SKILL.md)

MARKDOWN
---
name: release-check
description: Pre-release verification checklist for acme-billing-service.
  Use before tagging any release or when the user says "release check".
---

# Release check

Run, in order, and report each as pass/fail:
1. `npm test` β€” all green, no skipped tests.
2. `npm run build` β€” zero warnings.
3. `git log --oneline v$(cat VERSION)..HEAD` β€” summarize changes; flag any commit
   touching `migrations/` and verify a new migration file exists.
4. Confirm `CHANGELOG.md` has an entry for the new version.
5. If ANY step fails: stop, list failures, do NOT tag.
On full pass: propose the `git tag` command but do not run it β€” tagging requires the human.

Why it scores: model-invokable expertise (description says when to use), explicit ordered steps β€” instructions the model follows, not harness-enforced like a hook β€” and an explicit HITL boundary at the risky action.

Specimens: cookbook:.claude/skills/cookbook-audit/SKILL.md β€” a real SKILL.md.

Hooks (.claude/settings.json)

JSON
{
  "permissions": {
    "allow": ["Bash(npm run test:*)", "Bash(npm run build)", "Read(**)", "Edit(src/**)"],
    "deny": ["Read(.env*)", "Read(secrets/**)", "Bash(rm -rf*)"]
  },
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command",
        "command": "python3 .claude/hooks/block_dangerous.py" }]
    }],
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{ "type": "command",
        "command": "python3 .claude/hooks/format_file.py" }]
    }]
  }
}
PYTHON
# .claude/hooks/block_dangerous.py β€” exit 2 blocks the action; stderr goes to Claude
import json, re, sys
cmd = json.load(sys.stdin).get("tool_input", {}).get("command", "")
BANNED = [r"rm\s+-rf\s+/", r"git\s+push\s+--force", r"DROP\s+TABLE", r"curl[^|]*\|\s*sh"]
for pat in BANNED:
    if re.search(pat, cmd, re.I):
        print(f"Blocked by policy: matches '{pat}'. Propose a safer alternative.", file=sys.stderr)
        sys.exit(2)
sys.exit(0)

Formatter input: the runtime supplies JSON on stdin. This is an illustrative subset:

JSON
{"tool_name":"Edit","tool_input":{"file_path":"src/invoice.ts"}}

Formatter implementation: save as .claude/hooks/format_file.py. It expects Prettier already installed in the project; it does not download a formatter during a hook.

PYTHON
import json
import subprocess
import sys
from pathlib import Path

payload = json.load(sys.stdin)
root = Path.cwd().resolve()
raw = payload.get("tool_input", {}).get("file_path")
if not raw:
    sys.exit(0)
file_path = Path(raw).resolve()
if not file_path.is_relative_to(root):
    sys.exit("File is outside the project")
if file_path.suffix not in {".js", ".ts", ".jsx", ".tsx", ".json", ".css", ".md"}:
    sys.exit(0)
formatter = root / "node_modules" / ".bin" / "prettier"
if not formatter.exists():
    sys.exit("Project Prettier is not installed")
subprocess.run([str(formatter), "--write", str(file_path)], check=True)

Expected outcome: Prettier formats the edited file, or the hook reports a failure. The original edit has already happened because this is a PostToolUse handler. Verify the working directory in your runtime, and use CI formatting checks to cover edits outside this hook.

Why it scores: allow/deny lists limit how much damage a bad command can do (the blast radius); the PreToolUse guard is deterministic enforcement; the formatter hook applies style on every write without burning instructions β€” and because it drops the || true, a formatter failure surfaces instead of passing silently. Note the deny patterns are defense-in-depth, not the real boundary: shell syntax has many equivalent forms a blocklist cannot enumerate. Use appropriate runtime permissions and sandbox/network restrictions; an allowed command can itself run other code.

Specimens: cookbook:skills/.claude/settings.json β€” hooks in the wild, legacy shape (no matcher/hooks nesting β€” spot the drift).

Project MCP config (.mcp.json β€” checked into git)

JSON
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "//": "pin an exact version in real projects (…@x.y.z) β€” vet MCP servers like dependencies",
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    },
    "tickets": {
      "type": "http",
      "url": "https://mcp.internal.acme.com/tickets",
      "headers": { "Authorization": "Bearer ${TICKETS_TOKEN}" }
    }
  }
}

Why it scores: env interpolation (no secrets in git), plus one server of each of MCP's two transport types β€” a stdio server (a local subprocess Claude Code launches and talks to over standard input/output) for local tooling, and an HTTP server for a shared remote service β€” showing you know the transports and scopes. The "//" line is a reminder, not config: npx -y runs whatever version resolves today, so pin an exact version and vet MCP servers like any other dependency.

Specimens: the cookbook ships no .mcp.json β€” this sample is the reference shape; the scope rules are in Domain 4.

A slash command (.claude/commands/fix-issue.md)

MARKDOWN
---
description: Fix a GitHub issue end to end
argument-hint: <issue-number>
---
Fetch issue #$ARGUMENTS with the github MCP server. Restate the problem in two
sentences. Locate the responsible code. Write a failing test reproducing it.
Fix, run the full test suite, and prepare a PR description referencing #$ARGUMENTS.
Stop before pushing β€” show me the diff first.

Specimens: cookbook:.claude/commands/review-pr.md β€” a real command file Β· cookbook:.claude/commands/review-pr-ci.md β€” its CI twin.

A subagent (.claude/agents/reviewer.md)

MARKDOWN
---
name: reviewer
description: Read-only code reviewer. Use after significant edits or before PRs.
tools: Read, Grep, Glob
---
You are a strict senior reviewer. Never edit files. Review the diff for:
correctness, security (injection, secrets, authorization), performance, and test coverage.
Output: a numbered list of findings, each with file:line, severity, and a concrete fix.
End with a verdict: APPROVE or REQUEST_CHANGES.

Why it scores: own context window, read-only tool policy, explicit output contract β€” the subagent pattern done canonically.

Specimens: cookbook:.claude/agents/code-reviewer.md β€” a complete agent: config above, system prompt below.