An operator you respect posts their setup: claude --dangerously-skip-permissions, aliased to yolo, run on everything. Their argument is that permission prompts break flow and Claude rarely does anything bad. Both claims are true. The conclusion is still wrong for the machine they run it on, and by the end of this lesson you will be able to say precisely why, using the permission system's own mechanics rather than vibes about safety. Part 0 got Claude Code installed and authenticated; this lesson is about operating the three controls that shape every session: which model, which mode, which permissions.
The controls, and what each one actually decides
Claude Code has one native install path. The macOS, Linux, and WSL command:
curl -fsSL https://claude.ai/install.sh | bashThe npm package installs the same native binary through a per-platform dependency, so there is no meaningfully different "npm version". Verify with claude --version, and when anything feels off, claude doctor runs a deeper check of the install and configuration. Authentication happens in the browser on first launch; /login inside a session switches accounts. That is the whole setup story, and none of it is the interesting part. The interesting part is that after setup you are holding three independent dials, and most operators never touch two of them.
The model dial decides capability per dollar. The mode dial decides how often you are interrupted. The permission rules decide what is possible at all. Confusing the last two is the root of most permission pain: people who hate prompts reach for a looser mode when what they actually want is a tighter allowlist.
Models: aliases, not version numbers
You select models by alias, and the aliases are the operational interface. As of July 2026 the lineup in the docs: sonnet points at the latest Sonnet model for daily coding, opus at the latest Opus for heavier reasoning, haiku at the fast cheap end, and fable at Claude Fable 5, positioned for your hardest and longest-running tasks. Two meta-values matter more than people expect. default clears any override and reverts to the recommended model for your account type, which for subscription seats currently resolves to Sonnet 5. And best uses Fable 5 where your organization has access, falling back to the latest Opus. Fable 5 is never the default on any account type; you get it only by choosing it.
The alias worth a paragraph is opusplan: it runs Opus while you are in plan mode and switches to Sonnet automatically when you start executing. That split matches how the cost actually distributes. Planning is where deeper reasoning pays for itself, because a wrong plan multiplies into hundreds of wrong lines; execution against a good plan is mechanical enough for a cheaper model. If you only ever set one non-default model value, this is the defensible one.
Switching: /model in a session opens a picker where Enter saves the choice as your default for new sessions and s applies it to this session only. claude --model <name> at launch is session-scoped, which makes it the right tool for running different models in different terminals simultaneously. Resumed sessions keep the model they were saved with, so one terminal's /model choice does not silently change another's.
There is a second capability dial stacked on top of model choice: effort. /effort adjusts how much reasoning the model applies, from low through medium, high (the default on current models), and xhigh, with session-only max above that. The docs are refreshingly honest that max "may show diminishing returns and is prone to overthinking". For a single hard question mid-session, include the keyword ultrathink anywhere in the prompt; Claude Code recognizes it and requests deeper reasoning for that turn without changing your setting. Other phrasings like "think hard" are just words now; only ultrathink is a recognized keyword, per the model-config docs.
The operating position: run sonnet at default effort as your daily driver, and escalate deliberately, opusplan when the plan is the hard part, fable or opus when a task has already failed once on Sonnet and the failure was reasoning, not context. Escalating model because output "felt weak" without diagnosing why burns money on problems that a better dispatch would fix free.
Modes: how often the loop pauses for you
When Claude wants to edit a file or run a command, it pauses and asks. Permission modes control how often that pause happens. Cycle them with Shift+Tab; the current mode shows in the status bar. The docs' own table, condensed:
default: labeled Manual in the CLI. Reads run free; everything else prompts. Sensitive work, unfamiliar repos.acceptEdits: reads, file edits, and common filesystem commands (mkdir,touch,rm,mv,cp,sed) run without asking, but only inside your working directory. Iterating on code you are reviewing anyway.plan: reads only. Claude researches and proposes; nothing touches disk. Lesson 2.3 is built around this mode.auto: everything runs, with a separate classifier model reviewing actions in the background, blocking scope escalation, unrecognized infrastructure, and actions that look driven by hostile content Claude read. The docs label it a research preview and mean it.dontAsk: the inverse of auto. Anything that would prompt is auto-denied; only your explicit allow rules and read-only commands execute. Built for CI, where nobody is present to answer.bypassPermissions: everything runs, no checks. More on this below, because it deserves mechanics, not a scary label.
Permissions: rules the model cannot talk its way past
Here is the sentence that makes the whole system make sense, straight from the permissions doc: rules are enforced by Claude Code, not by the model. A CLAUDE.md instruction shapes what Claude tries; a permission rule changes what the harness allows. One is persuasion, the other is physics. When you need a guarantee, you want physics.
Rules come in three lists, and evaluation order is the part people get wrong: deny, then ask, then allow, first match wins, and specificity does not change the order. A broad Bash(aws *) deny blocks every matching call including ones that also match a narrower Bash(aws s3 ls) allow. Deny rules cannot carry exceptions. If you want most of a tool blocked with carve-outs, you must write narrow denies, not a broad deny plus narrow allows.
The syntax is Tool or Tool(specifier):
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)"
],
"deny": [
"Bash(curl *)",
"Read(./.env)",
"Read(./secrets/**)"
]
}
}Bash(npm run test *) is prefix matching. Read(./.env) uses paths, WebFetch(domain:example.com) scopes web fetches, and mcp__github matches every tool from an MCP server. One subtlety worth knowing before you need it: denying a bare tool name like Bash removes the tool from Claude's context entirely, so it never even sees it, while a scoped deny like Bash(rm *) leaves the tool available and blocks matching calls.
Where rules live follows settings precedence: managed settings (org policy, unoverridable), then command-line flags, then .claude/settings.local.json (yours, gitignored), then .claude/settings.json (the project's, committed), then ~/.claude/settings.json (yours, everywhere). A deny at any level beats an allow at any other, because all denies evaluate before all allows. Manage it all through /permissions, which shows every rule and which file it came from.
Two built-in floors operate under everything. Read-only tools (file reads, Grep, Glob) never prompt in any mode. And a set of protected paths (.git, .claude, shell rc files, lockfile configs, and more) are never auto-approved in any mode except bypassPermissions, guarding repository state and Claude's own configuration even when an allow rule technically matches.
Where it breaks: the skip-permissions question, answered honestly
Now the yolo alias. --dangerously-skip-permissions launches bypassPermissions mode: no prompts, no classifier, and as of v2.1.126 not even protected-path checks. The docs say plainly that it offers no protection against prompt injection or unintended actions, and they scope its use to isolated environments: containers, VMs, dev containers without internet access. The tool even refuses to start in this mode as root.
The honest version of the tradeoff: inside a disposable container, bypass is fine and genuinely pleasant; the blast radius is the container. On your laptop, the mode converts every piece of text Claude reads (a README, a web page, an error message crafted by someone else) into potential instructions with full write access to your machine and your credentials. The failure is not "Claude goes rogue". The failure is Claude obediently following instructions that were not yours. That is why the earlier distinction matters: prompt fatigue is real, but the fix is a curated allowlist plus acceptEdits, or auto mode's background classifier if your account has it. Those remove most interruptions while keeping a machine between untrusted text and your filesystem.
Annoyed by prompts, you alias --dangerously-skip-permissions and run it everywhere. Ninety-five percent of sessions are fine. The other five percent, nothing stands between a prompt-injected instruction and rm, curl, or your ~/.ssh.
Annoyed by prompts, you spend ten minutes in /permissions allowing Bash(npm run *), Bash(git status), Bash(git diff *), and your test runner, then work in acceptEdits. Routine work flows without prompts; anything novel still pauses. The interruption rate drops the same amount; the guarantee survives.
Lablab-2-1Plan-then-execute, then build your allowlist
Goal: Run one feature through plan mode deliberately, then configure a permission allowlist that removes your most frequent prompts without loosening the mode.
Prereqs: the agentic-practice repo from Lesson 0.2, Claude Code installed and authenticated.
- In the practice repo, run
claude --permission-mode plan. Dispatch a small real change: "Add an npm script called check that runs the test suite and reports pass or fail. Propose a plan first." Watch it read without writing. - When the plan appears, do not approve it yet. Press Ctrl+G to open it in your editor, change one detail (a filename, a step order), save, and return. Then approve with the option that reviews each edit manually.
- Approve or reject each edit as it comes. Note in
controls-lab.mdwhich prompts felt like signal and which felt like noise. - Run
/permissionsand read what is already there. Then create.claude/settings.jsonin the repo with an allow list covering exactly the noise prompts you recorded: probablyBash(npm run *)and a couple of git read commands. Add one deny rule:Read(./.env). - Restart the session (settings load at launch), switch to
acceptEditswith Shift+Tab, and dispatch a follow-up task. Confirm the allowed commands now run without prompting. - Prove the deny is physics, not persuasion: create a dummy
.envwith a fake value, then tell Claude directly to read.env. It should be blocked by the rule regardless of how the request is phrased.
Verify
- The Ctrl+G edit you made shows up in the executed plan.
/permissionslists your new rules and names.claude/settings.jsonas their source.- A previously prompting command (like your test run) executes without a prompt after step 5.
- The
.envread is denied even when you explicitly ask for it, and Claude reports the block.
>Troubleshooting
- Rules do not appear in
/permissions: check the JSON parses (a trailing comma is the usual culprit) and that the file is.claude/settings.jsoninside the repo, then restart the session. - The .env read succeeded: check the rule is in
deny, notallow, and that the path matches (Read(./.env)for a repo-root file). Also confirm you are not inbypassPermissions. - No permission prompt ever appears in step 1: you may already have broad allow rules in
~/.claude/settings.jsonfrom earlier experimentation. Read them; that audit is the lab working as intended.
Knowledge check
Knowledge check
Sources
- Set up Claude Code (install commands, native binary, claude doctor): https://code.claude.com/docs/en/setup (fetched July 2026)
- Model configuration (aliases, default resolution, opusplan, effort levels, ultrathink): https://code.claude.com/docs/en/model-config (fetched July 2026)
- Choose a permission mode (mode table, Shift+Tab cycle, plan approval, auto mode classifier, bypassPermissions scope, protected paths): https://code.claude.com/docs/en/permission-modes (fetched July 2026)
- Configure permissions (rule syntax, deny-ask-allow order, settings precedence, enforcement vs. context): https://code.claude.com/docs/en/permissions (fetched July 2026)