AI EngineeringZero to ProductionHome·About·Contact
Anthropic Skills · Chapter K1

Claude Code in Action

Driving Claude Code well over a real, extended task: the steer loop, CLAUDE.md, scoped permissions (with a runnable matcher), verify-don't-trust, and standardizing it for a team.

⏱️ ~3 hours🧪 9 labs🎯 Beginner→Tech-lead
🌱 Start here — from zero Claude Code, from scratch — if you've only chatted with an AI, this shows what it looks like to make one WORK — steer it, configure it, trust it.

Claude Code is an AI that lives in your terminal and can read files, run commands, and edit code — in a loop you influence. This chapter climbs from your first steered session to configuring a repo, controlling permissions, verifying work, and setting team standards. Claude Code commands run in your terminal; the Python blocks (a permission model, a verification gate) run offline.

The words you'll hear (in plain terms):

TermWhat it actually means
agentan AI that takes actions in a loop (act → observe → decide), not one reply.
steeringguiding the loop: plan approval, scoping, interrupting, rewinding.
CLAUDE.mda project file Claude reads every session: conventions, commands, guardrails.
permission modewhat Claude may do without asking (read/edit/run).
verificationchecking Claude's work (tests, diff) instead of trusting it.

What you need before starting:

  • The C3 intro is the gentle first look.
  • Claude Code installed (or read along to learn the model).
  • Basic command-line comfort.

New to the topic? Read this box, then take the chapters in order — each section is tagged essentialexpert so you always know the depth you're at.

Learning objectives

  • Explain why extended sessions drift and how the steer loop counters it.
  • Configure a project with CLAUDE.md and scoped permissions.
  • Verify work instead of trusting it.
  • Set the steering + config standards a team shares.
▶ Runnable companionCode saved under code/ak1-claude-code-in-action/. Python runs offline; configs are ready to use.

1 · Steering beats prompting essential

With a chat model you write one good prompt. With Claude Code you run a loop you influence: approve a plan, narrow scope, interrupt when it drifts, rewind when it goes wrong. The skill is steering, not one-shot prompting.

You: goal intent Plan before code Act (edit/run) tools Verify tests/output You: steer course-correct
🗺️ How to read this diagram

This is the whole idea of Claude Code in one picture. Unlike a chatbot where you send one prompt and get one answer, working with Claude Code is a loop you stay inside of and keep nudging. Read the five boxes left to right, then notice it repeats.

  • You: goal — you start by saying what you want (your intent), e.g. "add pagination, don't touch auth".
  • Plan — before writing any code, Claude proposes an approach. This is where a wrong assumption is cheap to fix.
  • Act (edit/run) — Claude uses its tools: it edits files and runs commands in your terminal.
  • Verify — you (or a test) check the result: did the tests pass? does the output look right?
  • You: steer — you course-correct: approve, narrow the scope, interrupt, or rewind — then the loop runs again.

In short: The arrows never stop at "Act" — they always come back to you. That feedback loop (goal → plan → act → verify → steer → repeat) is what "steering" means, and it's the skill this whole chapter teaches.

2 · The extended-session problem essential

A one-line prompt is easy. A two-hour refactor across twenty files is where it drifts: the context window fills, early decisions get forgotten, the agent wanders. Understanding the loop's lifecycle is what lets you steer a long task without it going sideways.

3 · Plan first, then act essential

For anything non-trivial, get a plan before code — it catches wrong assumptions when they're cheap to fix.

A well-steered task

  1. State the goal and constraints ("add pagination; don't touch auth").
  2. Ask for a plan; read it. Correct a wrong step now, not after 12 edits.
  3. Approve; let it work in a scoped area. Watch the first tool calls to confirm direction.
  4. Interrupt (Esc) and re-steer if it drifts: "stop — wrong file."
  5. Verify yourself (run tests, read the diff). Only then move on.
Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
prompt · plan-mode request
steering.md> Plan a refactor that extracts the retry logic from api.py into a reusable
> decorator. List the files you'll touch and the risks. Do NOT write code yet —
> I'll approve the plan first.

# Claude explores, proposes an approach, and waits. You approve or correct
# BEFORE any edits. Enter plan mode explicitly, or just ask for a plan.
▶ How this works

This is what you actually type to Claude Code to make it plan before it touches any code. The > at the start of each line is just the prompt shown in the terminal — you type the words after it. The whole message is one instruction.

  1. The first two lines state the goal precisely: extract the retry logic from api.py into a reusable decorator. A vague goal gets a vague plan.
  2. "List the files you'll touch and the risks" asks Claude to show its thinking first so you can catch a bad idea before it happens.
  3. "Do NOT write code yet — I'll approve the plan first" is the key line: it puts Claude in plan mode, so it explores and proposes but waits for your OK before editing.
  4. The # comment underneath is a note for you, the reader — it explains that Claude will explore, propose, and pause. You approve or correct before any files change.

What the output means: Claude replies with a written plan (files, steps, risks) and then stops. Nothing on disk changes until you say go.

Try this: Whenever a task is bigger than a one-line fix, add "plan first, don't code yet" to your request. Reading a plan takes seconds; undoing twelve wrong edits does not.

4 · Configure the project — CLAUDE.md intermediate

CLAUDE.md is project memory Claude reads every session: conventions, commands, and constraints. It's the single highest-leverage file for quality — it stops you re-explaining context every time and makes the whole team's Claude behave consistently.

config · a project CLAUDE.md
CLAUDE.md# CLAUDE.md — read at the start of every session

## Commands
- Test:  `pytest -q`
- Lint:  `ruff check .`
- Run:   `python -m app`

## Conventions
- Python 3.12, type hints on public functions.
- Never edit files under `vendor/` — generated.
- Prefer stdlib; a new dependency needs a note in the PR.

## Guardrails
- Do NOT commit or push unless I explicitly ask.
- Ask before deleting files or changing the DB schema.
▶ How this works

CLAUDE.md is a plain text file you put in your project. Claude Code reads it automatically at the start of every session, so it's how you tell Claude the things you'd otherwise have to repeat every single time — your commands, your rules, your guardrails.

  1. The ## Commands section lists how your project runs its test, lint, and start commands. Now Claude runs pytest -q instead of guessing.
  2. ## Conventions encodes house style: Python 3.12, type hints, never edit vendor/, prefer the standard library. These stop Claude from writing code that doesn't fit your repo.
  3. ## Guardrails are hard don't rules: don't commit or push unless asked; ask before deleting files or changing the database. These are the safety limits.
  4. The headings (##) are Markdown — just a readable way to organize the file. Claude reads the whole thing as context, so clear, short bullets work best.

Try this: Add one line to the Conventions section for a rule you keep repeating to Claude. That one line means you never have to say it again — and everyone on your team gets it too.

5 · Permissions — control what it can do intermediate

Claude Code asks before running commands or editing, governed by a permission mode + an allow/deny list in settings.json. Tightening this lets it move fast on safe actions while gating dangerous ones.

config · .claude/settings.json
settings.json{
  "permissions": {
    "allow": ["Bash(pytest:*)", "Bash(ruff:*)", "Read(*)", "Edit(src/**)"],
    "deny":  ["Bash(rm -rf:*)", "Bash(git push:*)", "Edit(vendor/**)"]
  }
}
▶ How this works

This file (.claude/settings.json) is where you decide what Claude may do without stopping to ask you. It's JSON — a list of names and values in curly braces. Two lists do all the work: allow and deny.

  1. "allow" lists safe actions Claude can take freely. Bash(pytest:*) means "any pytest command"; Read(*) means "read any file"; Edit(src/**) means "edit anything under src/". The * is a wildcard — it matches anything.
  2. "deny" lists actions Claude must never do: Bash(rm -rf:*) (dangerous deletes), Bash(git push:*) (pushing code), and Edit(vendor/**) (editing generated files).
  3. Anything not on either list falls through to "ask me first" — the safe default. You widen the allow-list as you learn which actions are safe.

What the output means: With this file in place, Claude runs tests and edits src/ without interrupting you, but stops and refuses to push or delete.

Try this: The next lab turns these exact rules into runnable code so you can see how a single action gets matched to ALLOW, DENY, or ASK.

6 · Advanced — model the permission decision advanced

How does an allow/deny list actually decide? Modeling it makes the rules unambiguous — and it's the logic a policy check would run.

Python · a permission matcher (runs)
permission_model.pyimport fnmatch

def decide(action, allow, deny):
    """deny wins over allow; unmatched -> ask the human."""
    if any(fnmatch.fnmatch(action, d) for d in deny):
        return "DENY"
    if any(fnmatch.fnmatch(action, a) for a in allow):
        return "ALLOW"
    return "ASK"

allow = ["Bash(pytest*)", "Read(*)", "Edit(src/*)"]
deny  = ["Bash(rm -rf*)", "Bash(git push*)", "Edit(vendor/*)"]
for act in ["Bash(pytest -q)", "Edit(src/app.py)", "Edit(vendor/x.py)",
            "Bash(rm -rf /)", "Bash(curl evil.com)"]:
    print(f"{decide(act, allow, deny):5}  {act}")
ALLOW  Bash(pytest -q)
ALLOW  Edit(src/app.py)
DENY   Edit(vendor/x.py)
DENY   Bash(rm -rf /)
ASK    Bash(curl evil.com)
▶ How this works

This tiny Python program is the decision logic behind the allow/deny lists you just saw — written out so the rules are unambiguous. Given an action, it returns one of three answers: DENY, ALLOW, or ASK.

  1. fnmatch is a standard-library tool that matches text against a wildcard pattern (so Bash(pytest*) matches Bash(pytest -q)). It's the same idea as the * in the settings file.
  2. The function checks the deny list first: if any deny pattern matches, it returns DENY immediately. This is the "deny wins over allow" rule from the docstring.
  3. Only if nothing denied it does it check allowALLOW. If neither list matches, it falls through to ASK — hand the decision to a human.
  4. The for loop at the bottom runs five sample actions through decide() and prints the verdict for each, lined up in a neat column.

What the output means: Five lines: pytest and editing src/ are ALLOW; editing vendor/ and rm -rf / are DENY; the unknown curl command is ASK — exactly matching the settings you'd expect.

Try this: Add "Bash(curl*)" to the deny list and re-run. The curl line flips from ASK to DENY — proof that adding a deny rule tightens the gate.

Deny-wins + default-ask is the safe designUnmatched actions default to ASK (a human decides), and deny always beats allow. That ordering means a broad allow can't accidentally permit a dangerous action you explicitly denied — the same principle as IAM and firewall rules.

7 · Advanced — verify, don't trust advanced

The single habit that makes agentic coding reliable: verify the work. An agent that says "done" has proven nothing until you see green tests and a clean diff.

Python · make verification a gate (runs)
verify_gate.pydef accept_change(tests_pass, diff_reviewed, touched_forbidden):
    reasons = []
    if not tests_pass:        reasons.append("tests not green")
    if not diff_reviewed:     reasons.append("diff not reviewed")
    if touched_forbidden:     reasons.append("touched a forbidden path")
    return (not reasons), reasons

print("accept?", accept_change(True, True, False))
print("accept?", accept_change(True, False, True))
accept? (True, [])
accept? (False, ['diff not reviewed', 'touched a forbidden path'])
▶ How this works

The most important habit in agentic coding is verify, don't trust: an agent saying "done" proves nothing. This function turns "is this change acceptable?" into a clear yes/no with reasons, so you can't wave work through on vibes.

  1. accept_change takes three facts about the change: did the tests pass, did you review the diff, and did it touch a forbidden path?
  2. Each if adds a human-readable reason to the reasons list when something is wrong. No reasons means nothing is wrong.
  3. It returns a pair: (not reasons) is True only when the reasons list is empty (accept it), plus the list of reasons so you know why if it was rejected.
  4. The two print calls test it: one perfect change, one broken change (diff not reviewed and a forbidden path touched).

What the output means: The first line is (True, []) — accepted, no problems. The second is (False, ['diff not reviewed', 'touched a forbidden path']) — rejected, with the exact reasons spelled out.

Try this: Flip the first True to False (tests failing) and re-run — watch "tests not green" appear and the change get rejected. This is how you make "done" mean "actually verified".

Automate verification so you can't skip itVerification you have to remember is verification you'll skip under pressure. A Stop hook that runs the tests (K2) makes "done" mean "tests green" automatically — the config does the discipline for you.

8 · Professional — a repeatable session workflow professional

Your per-task Claude Code loop

  1. Pull latest; state the goal + constraints.
  2. Plan-first for anything non-trivial; approve/correct the plan.
  3. Let it work scoped; watch early tool calls.
  4. Verify: run tests, read the full diff, check no forbidden paths.
  5. Commit yourself with a clear message (DF2); never auto-push.

9 · Tech-lead — standardize steering across the team tech-lead

A lead commits a shared CLAUDE.md + settings.json so every engineer's Claude follows the same conventions, guardrails, and permissions. Consistency scales quality — and a config check keeps it honest.

Python · audit a repo's Claude config (runs)
config_audit.pydef audit_config(cfg):
    problems = []
    if "deny" not in cfg.get("permissions", {}):
        problems.append("no deny-list -> risky actions ungated")
    if "Bash(git push*)" not in cfg.get("permissions", {}).get("deny", []):
        problems.append("push not denied -> agent could push unreviewed")
    if not cfg.get("has_claude_md"):
        problems.append("no CLAUDE.md -> conventions not shared")
    if not cfg.get("stop_hook_tests"):
        problems.append("no test-on-stop hook -> verification is manual")
    return problems

weak = {"permissions": {"allow": ["Edit(*)"]}, "has_claude_md": False}
strong = {"permissions": {"allow":["Edit(src/*)"], "deny":["Bash(git push*)"]},
          "has_claude_md": True, "stop_hook_tests": True}
print("weak repo:", audit_config(weak))
print("strong repo:", audit_config(strong) or "compliant")
weak repo: ['push not denied -> agent could push unreviewed', 'no CLAUDE.md -> conventions not shared', 'no test-on-stop hook -> verification is manual']
strong repo: compliant
▶ How this works

A tech lead wants every engineer's Claude configured safely, not just their own. This function audits a repo's config and returns a list of problems — an automated check that the safety pieces are actually in place.

  1. It's handed a cfg dictionary describing the repo's setup, and builds a problems list as it finds gaps.
  2. The four checks map to the four safety habits from this chapter: is there a deny-list? is git push denied? is there a CLAUDE.md? is there a test-on-stop hook (automatic verification)?
  3. Each missing piece appends a plain-English problem explaining the risk, e.g. "no CLAUDE.md -> conventions not shared".
  4. It's run on two repos: a weak one (allow-everything, no CLAUDE.md) and a strong one that follows every rule.

What the output means: The weak repo prints a list of three problems; the strong repo prints compliant (its problem list was empty, so [] or "compliant" shows the word instead).

Try this: Delete one good setting from the strong dictionary (say remove stop_hook_tests) and re-run — that repo stops being "compliant" and the matching problem appears. That's the audit doing its job.

Config is how a lead scales good habitsOne committed .claude/ tree means steering discipline, guardrails, and verification are the default for everyone who clones the repo — not something each engineer re-learns. That's the tech-lead multiplier.

Exercise AK1.1 — Steer + configure a real task

Context: This is the full loop of a well-run Claude Code session in miniature: steer the task, pin down conventions and permissions in config, plan before editing, and refuse to accept changes until they're proven.

Your task: Take a small refactor and run it end to end — a scoped CLAUDE.md and settings.json, plan-first execution with one mid-course re-steer, and a green-tests-plus-diff-review gate before accepting.

Requirements:

  • The CLAUDE.md includes the project commands and at least one "don't touch" rule
  • The settings.json is scoped — read/test tools allowed, irreversible actions handled
  • Run it plan-first and interrupt once to re-steer mid-task
  • Require a green test run and a diff review before accepting the change (use the accept_change logic)
  • Run config_audit on your setup and address what it flags

💡 Hint: Do the smallest real refactor you can find — the value is in exercising the whole steer → configure → plan → verify loop, not the size of the change.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Steer with a specific instruction, not a vague oneBeginner

Context: In day-to-day Claude Code work, the difference between a session that lands and one that drifts is almost always how the request was framed — steering, not one clever prompt, is the real skill.

Your task: Rewrite a vague ask like "make the tests better" into a steered instruction that names the file, the specific gap, and the guardrails.

Requirements:

  • The rewrite names a concrete file (or files) to work in
  • It states the specific goal, not a mood — e.g. which missing test paths to add
  • It sets an explicit constraint / no-go zone (e.g. don't touch production code, no new dependencies)
  • The result is verifiable — you can check whether Claude did exactly that
  • Contrast the vague and steered versions side by side so the difference is visible

💡 Hint: Think about what a teammate would need to execute the task without asking a follow-up question — file, gap, and guardrail.

Show solution

Vague prompts invite drift; steered instructions bound the work:

Vague:   "make the tests better"

Steered: "In tests/test_auth.py, add cases for the expired-token and
          missing-header paths. Use the existing pytest fixtures; don't
          touch the production code or add new dependencies."

Naming the file, the specific gap, and the guardrails ("don't touch production code") turns an open-ended request into one Claude can execute without guessing — and one you can verify. Steering is giving direction throughout, not a single perfect prompt up front.

Exercise 2 · Plan first, then actIntermediate

Context: Reviewing a plan is far cheaper than reviewing a diff: catching a wrong approach before any code exists costs one sentence instead of a revert, which is why the plan gate is the intermediate habit worth building.

Your task: Describe the plan-first workflow for a multi-file change and explain why gating on the plan matters before any edit is made.

Requirements:

  • Show how to enter plan mode in the Claude Code CLI (and note UI shortcuts drift — verify in the docs)
  • The plan is requested explicitly as plan-only: list files and approach, do not edit yet
  • You read and correct the approach before code is written, not after
  • Only after approval does Claude execute the agreed plan
  • Explain the payoff: redirecting a plan is cheaper than reverting a diff

💡 Hint: The key insight is when you intervene, not the exact keystroke — put the review before the first edit.

Show solution

The plan-first loop, in practice (Claude Code CLI):

1. Enter plan mode (Shift+Tab to cycle to "plan", or start with a
   "plan only, don't edit yet" instruction).
2. Ask: "Plan how to add rate limiting to the API layer. List the files
   you'd change and the approach — do not edit yet."
3. Read the plan. Correct the approach BEFORE code exists
   ("use the existing middleware, not a new decorator").
4. Approve; Claude executes the agreed plan.

Reviewing a plan is far cheaper than reviewing a diff — you catch a wrong approach before any code is written, when redirection costs one sentence instead of a revert. (Exact key to toggle plan mode: verify in the Claude Code docs, as UI shortcuts drift.)

Exercise 3 · Write a CLAUDE.md that actually steersAdvanced

Context: A CLAUDE.md is read every session, so every line spends context; a tight, load-bearing one steers Claude away from the mistakes it keeps making, while a bloated one dilutes the signal.

Your task: Write a focused CLAUDE.md that captures the conventions Claude keeps getting wrong, and explain what does not belong in it.

Requirements:

  • Capture stable, repeatedly-needed facts: tooling/package manager, test layout and command, the fallible-return convention
  • Include an explicit Do not section (e.g. don't add deps without asking, don't edit generated code)
  • Keep it short — state why a bloated file hurts because it is read every session
  • Explain what to leave out (whole-architecture dumps, prose novels)
  • The examples are concrete conventions, not generic advice

💡 Hint: Ask yourself which facts Claude rediscovers or gets wrong every session — those, and only those, earn a line.

Show solution

CLAUDE.md is persistent project steering — keep it tight and load-bearing:

# CLAUDE.md

## Conventions
- Package manager is `pnpm`, never `npm`.
- Tests live next to source as `*.test.ts`; run with `pnpm test`.
- Use the existing `Result<T>` type for fallible functions; don't throw.

## Do not
- Don't add dependencies without asking.
- Don't edit `generated/` — it's built from the schema.

Put stable, repeatedly-needed facts (tooling, conventions, no-go zones) that Claude otherwise rediscovers or gets wrong. Don't paste the whole architecture or a novel — a bloated CLAUDE.md dilutes the signal. It is read every session, so every line costs context.

Exercise 4 · Model the permission decisionExpert

Context: Permissions are what let Claude act without a prompt on every step; getting the allow / ask / deny precedence right is what makes an agent both fast and safe.

Your task: Write a pure-Python model of the allow / ask / deny decision for a tool call, given an allowlist, a denylist, and a default mode.

Requirements:

  • The function takes the tool, an allow set, a deny set, and a default mode
  • Denylist wins first — a denied tool is blocked even if also allowed
  • Allowlisted read-only tools resolve to allow (run without prompting)
  • Anything unmatched falls back to the session's default mode (ask by default)
  • Demonstrate all three outcomes with example tool calls
  • Note that exact settings.json permission syntax should be verified in the docs

💡 Hint: Model it as an ordered check: deny, then allow, then default — the order is the whole behavior.

Show solution

Model the precedence the CLI applies (deny wins, then allow, then default):

def decide(tool, allow, deny, default_mode):
    # denylist always wins
    if tool in deny:
        return "deny"
    if tool in allow:
        return "allow"
    # otherwise fall back to the session's default mode
    return default_mode          # "ask" (default) | "allow" | "deny"

allow = {"Read", "Grep", "Bash(pnpm test)"}
deny  = {"Bash(rm -rf *)", "Bash(git push)"}
print(decide("Read", allow, deny, "ask"))            # allow
print(decide("Bash(git push)", allow, deny, "ask"))  # deny
print(decide("Edit", allow, deny, "ask"))            # ask (default)

Read-only tools go on the allowlist so they run without prompting; irreversible ones (force-delete, push) go on the denylist so they are always blocked; everything else falls to the default mode. Exact settings.json permission keys/syntax: verify in the Claude Code docs.

Exercise 5 · Verify, don't trustProfessional

Context: "All tests pass" from a long session is a claim, not evidence; the professional habit is closing the loop with a check the agent didn't author, because the real risk in extended runs is silent scope creep.

Your task: Design the verification step for a session that made 12 edits and claims green, so you trust the tooling's output rather than the narration.

Requirements:

  • Run the test suite yourself (or have Claude show raw output + exit code), not a summary
  • Run the linter as an independent gate
  • Use git diff --stat to confirm only the expected files changed
  • Read the diff by hand for anything security- or data-sensitive
  • Only commit on green; on failure, feed the real failing output back and re-steer

💡 Hint: The point is independence — verify with something the agent did not write, and read the diff scope, not just the pass/fail.

Show solution

Close the loop with an independent check the agent didn't author:

# 1. Run the checks yourself (or make Claude run them and show raw output):
pnpm test            # exit code + real output, not a summary
pnpm lint
git diff --stat      # confirm ONLY the files you expected changed

# 2. Read the diff of anything security- or data-sensitive by hand.
# 3. If green: commit. If not: feed the failing output back and steer.

"All tests pass" is a claim, not evidence — run the suite and read the exit code and diff yourself. The extended-session risk is silent scope creep (files you didn't expect changed), which git diff --stat surfaces immediately. Trust the tools' output, not the narration.

Exercise 6 · Standardize a repeatable session workflow for the teamIndustry scenario

Context: A team standard only works if the safe path is the easy path: irreversible and quality gates belong in shared config so they can't be skipped, while judgement steps stay advisory.

Your task: As tech lead, define a repeatable per-task Claude Code workflow as a checklist a new engineer can run, marking each step enforced vs advisory.

Requirements:

  • Written as an ordered per-task checklist (branch first, steer, plan, work, verify, commit/PR)
  • Each step is labelled enforced or advisory
  • Irreversible/quality gates are enforced in shared config — hooks, settings.json permissions, branch protection
  • Judgement steps (steering, planning) are kept advisory
  • The standard ships with the repo via version-controlled CLAUDE.md + settings.json
  • Note that exact hook/settings keys should be verified in the docs

💡 Hint: Split the checklist by what a human can forget (advisory) versus what must never be skippable (enforce it in config).

Show solution

A team playbook that makes good sessions the default:

Team Claude Code workflow (per task)
  1. Branch first (never work on the default branch).          [enforced: hook]
  2. Steer: state file(s), goal, and constraints up front.     [advisory]
  3. Plan mode for any change touching >1 file; approve plan.   [advisory]
  4. Let it work; keep read tools on the allowlist,            [enforced: settings.json]
     push/delete on the denylist.
  5. Verify: run tests + lint + `git diff --stat` yourself.    [enforced: PreToolUse hook on commit]
  6. Commit with a real message; open a PR for review.         [enforced: branch protection]

Enforce the irreversible/quality gates in shared config (permissions, a commit-time hook, branch protection) so they can't be skipped; keep the judgement steps (steering, planning) advisory. A shared, version-controlled CLAUDE.md + settings.json makes the team's standard the path of least resistance. Exact hook/settings keys: verify in the docs.

✓ Checkpoint — you can move on when you can…

  • Explain drift and counter it with the steer loop.
  • Configure CLAUDE.md + scoped permissions.
  • Make a verified test run the definition of done.
  • Standardize steering/config for a team and audit it.

Knowledge check check yourself

✓ Knowledge check

The lesson claims "steering beats prompting" for Claude Code. What is fundamentally different about working with an agent loop versus a chat model, and how does the extended-session problem arise?

Show answer
A chat model needs one good prompt; Claude Code runs an act-observe-decide loop you keep nudging (approve a plan, scope, interrupt, rewind). Over a long task the context window fills and early decisions get forgotten, so the agent drifts — steering the loop is what counters that.
✓ Knowledge check

In the decide permission matcher, deny is checked before allow and unmatched actions return ASK. Why is "deny-wins plus default-ask" called the safe design?

Show answer
Checking deny first means a broad allow rule can never accidentally permit something you explicitly forbade, and defaulting unknown actions to ASK hands novel/risky cases to a human — the same least-privilege principle as IAM and firewall rules.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in