Deciding What a Session Is Allowed to Do
The founding charter
The core problem: every permission prompt spends a little of your attention, and by the thirtieth approval in a session you've stopped reading before you say yes. The fix isn't discipline — it's deciding, calmly and in advance, what a session may do freely, what it must ask about, and what it may never do, written into a file rather than negotiated in the moment.
That file lives in three places, layered from general to specific: a user-level settings file that travels with you across every project, a committed project-level file that belongs to the whole team and shows up in code review, and an uncommitted local file for machine-specific quirks. More specific layers override broader ones, though a managed policy file (the kind an IT department deploys) sits above all of them and can't be overridden.
Inside these files, permission rules sort into three lists. Allow lets a matched action through without a prompt — pure convenience, buying back attention for boring, repeatable calls like running tests or reading files. Deny blocks an action outright with no prompt and no override, reserved for anything irreversible: touching production data, reading credentials, force-pushing or rewriting git history. Ask sits in between and always interrupts. Deny beats everything; ask beats allow. Applied to a typical TypeScript/Next.js/Postgres project, this produces a short, deliberate file — narrow allow rules for test, typecheck and lint commands, ask rules for writes outside the repo and package installs, deny rules for env files, cloud credentials, and destructive git or database commands.
The payoff is counterintuitive: locking things down doesn't feel restrictive, it makes the prompts that remain actually mean something, because the noise is gone.
The same logic scales up through plan mode, which stops a session from editing anything while it reads, traces and proposes — useful for unfamiliar code or multi-file changes, skippable for one-line fixes. An approved plan is not a contract; work in progress can reveal files the plan never listed, so the habit is to compare the plan's file list against what actually changed. Built-in commands like permissions, config, context, and status let you inspect and edit the live configuration mid-session rather than restarting — which matters because configuration drift is real: a rule quietly added to a local file weeks ago can silently override what the team believes is running. Checking the resolved permissions at the start of a session catches this before it costs anything.
The show's news beat, skewed hard
A new restricted mode for the command-line tool strips out command execution, code running and web fetching, and confines edits to the working directory — and it ignores your settings files entirely, offering a hard ceiling for working with untrusted code. Authentication can now log in directly against console billing alongside SSO and email options. Smaller additions include a readline-style keybinding option, per-prompt frontmatter controls for effort level and cache duration, and a statusline metric exposing rolling five-hour and seven-day rate-limit usage. A native VS Code extension brings inline diffs and sidebar progress, and community projects bundling sub-agents, skills and hooks — including one collection called Everything Claude Code and a planning-focused framework called Superpowers — offer working examples of the extension layer ahead.
There is a moment in every long session that you should learn to recognize, because it is the exact moment your safety net disappears.
The first few times Claude Code asks for permission to run something, you read the request. All of it. You look at the command, you think about what it touches, and then you approve it. Prompt five, same thing. Prompt twelve, you are still reading, though a little faster now. Somewhere around prompt thirty, the approval has become a reflex. Your eyes land on the box, your finger presses the key, and the command runs. You did not read it. You were thinking about the actual problem, which is where your attention belongs, and the permission prompt had become furniture.
The tell is very specific, and it is worth memorizing: you notice you have stopped reading the diff before you say yes. Not "I made a bad call." Not "I approved something scary." Just the quiet realization that the last several approvals happened without a decision behind them.
Here is the part people get wrong. The fix is not more willpower. You cannot solve this by resolving to read more carefully, because the thing that broke was your attention budget, and attention does not respond to resolutions. Every prompt you have to evaluate spends some of it. If a session asks you thirty times a day whether it may run the test suite — a thing you would approve every single time, without hesitation, forever — then thirty units of your judgment went to a decision that required none, and there is that much less left for the request that actually deserved a pause.
So the move is to get the decision out of the moment entirely. Not out of your hands: out of the moment. You decide once, calmly, sitting down with your coffee and no half-finished refactor in your head, what this session may do freely, what it must always ask about, and what it may never do at all. You write that down in a file. Then, during the session, the boring approvals stop happening and the ones that remain are the ones you meant to think about.
A session's power is not a feeling you have about it. It is a file.
Let's build that file from the ground up, because it lives in more than one place, and the places matter more than most people realize.
The first place is your own home directory. There is a Claude directory under your home folder, and inside it a settings file — the user-level one. Whatever you put there travels with you. Every project you open on this machine inherits it. This is where your personal habits belong: preferences about how the session behaves that have nothing to do with any particular codebase, and permissions you are comfortable granting yourself in every repository you touch.
The second place is inside the repository. In the project root there is a Claude directory too, and inside it a settings file — the project-level one. This one you commit. That is the whole point of it. When someone clones the repo, they get your permission decisions along with your test config and your lint rules, because those decisions are part of how this codebase is worked on. If you have decided that in this project nothing may ever touch the production database, that is not a personal preference. That is a property of the project, and it should be in version control where a code review can see it change.
The third place is the one people forget, and it is the one that will eventually bite you. Alongside the committed project settings there is a local settings file — same directory, and the filename carries "local" in it. That one is not committed; it belongs in your ignore file and usually gets put there for you. It exists for the legitimate case where one developer's machine needs something the team's does not. Maybe you have a helper script at a path nobody else has. Fine. Put it in the local file, where it cannot leak into anyone else's session.
Now, how do three files combine when they disagree? Think of them as layers, stacked from general to specific. The user-level file is the broadest, the committed project file sits on top of it, and the local file sits on top of that. The more specific layer wins. So a setting you have chosen personally can be overridden by the project you are currently in, and a setting the project chose can be overridden on your machine alone. Managed policy files, the kind an IT department deploys on a company laptop, sit above all of these and cannot be overridden at all — which is exactly what an organization wants from a policy.
Notice what that stacking implies about the local file. It is the highest layer a developer controls, which makes it the most convenient place to put things and the most dangerous. Hold that thought; we come back to it at the end, because it is the second form of today's pitfall.
Inside any of these files, the block that concerns us is the one keyed permissions. It holds lists, and each entry in a list is a rule. A rule names a tool, and optionally narrows it down to a specific use of that tool. So a rule might name the Read tool broadly, or it might name the Bash tool and then, in parentheses, the specific command shape it applies to — Bash with npm test in the parentheses, for example. That parenthesized part is what turns "may run shell commands" into "may run this shell command," which is the entire difference between a useful permission and a blank check.
Rules go into three lists, and the three lists produce the three things that can happen when the session wants to do something. There is allow, and a rule there means the session just does it and never interrupts you. There is ask, and a rule there means the session stops and shows you the request even if something else would have permitted it. And there is deny, and a rule there means the answer is no, with no prompt offered and no way to say yes in the moment.
The ordering is the single most important thing in this chapter, so let me state it flatly: deny beats everything. If a rule in your deny list matches, nothing in any allow list anywhere in the stack can rescue it. Ask beats allow. Allow only wins when nothing else matched.
Sit with what that means for how you build your configuration. The allow list is convenience. That is all it is. It buys back your attention by removing prompts you would have approved anyway. If you got a rule wrong in your allow list, the cost is that you got interrupted for something you did not need to be interrupted for, or you did not get interrupted for something mildly boring. Annoying, recoverable.
The deny list is the safety net. It is the only part of this file that protects you from yourself at prompt thirty, when you have stopped reading. So the deny list should be short, specific, and about consequences you genuinely cannot undo — and it should be in the committed project file, where the whole team gets it and where changing it requires a pull request that somebody looks at.
Let's make this real against the app we have been working in — the TypeScript and Next.js codebase with Postgres behind it.
Start with what you would always approve. Reading files in the repo, obviously. Searching the codebase. Running the test suite. Running the type checker. Running the linter. These are the commands that make up the actual rhythm of work, and there has never once been a version of you who wanted to be asked about them. Into allow: the Read tool, the search tools, and Bash rules narrowed to your test command, your typecheck command, and your lint command as they are actually spelled in your package scripts. Not "Bash." Never bare "Bash" in an allow list. The narrowed version.
Then decide what should stop and ask. Anything that writes outside the repository is the clean line here, and it is a good line because it maps onto something you can hold in your head. Inside the project directory, edits are the job; the git history is your undo. Outside it, you have no undo, and the session has no business going there without a conversation. Installing packages belongs in ask too, in most projects — not because installing a package is dangerous in itself, but because adding a dependency is a decision with a long tail, and it deserves three seconds of a human being.
Then the deny list, and keep it small so that every line in it means something. Anything that reaches production data: your production connection string, any command shaped like a psql call against the production host. Anything touching credentials: your environment file with the real secrets in it, the directory where your cloud credentials live, your shell history. And anything that rewrites git history where it hurts — a force push, a hard reset that throws away work you have not pushed. Those are the undo-less operations. If the session cannot do them, then the worst case at prompt thirty is a mess in your working tree, and a mess in your working tree is a five-minute problem.
So the committed project settings file for this app has a permissions block with three lists in it. The allow list holds Read, the search tools, and Bash narrowed to the test, typecheck and lint commands. The ask list holds writes outside the working directory and package installation. The deny list holds reads of the environment file and the cloud credentials directory, and Bash rules matching the force push, the hard reset, and any invocation against the production database host. That file is maybe twenty lines. It gets committed. It goes into the repo the same day the project memory file we wrote in the starter episode went in, and for the same reason: it is knowledge about how this codebase is worked on, and it should not live in one person's head.
Now here is the part that surprises people the first time they do it properly, and it is the reason to do it at all.
You expect a permissions file to feel restrictive. It does the opposite. The session stops asking you about the boring things — which was the vast bulk of the asking — and so the next time a permission prompt appears on your screen, it is genuinely unusual. It is something you did not pre-authorize. And because it is unusual, you read it. Your attention comes back, not through discipline, but because the noise floor dropped and the signal became audible again. A well-tuned allow list is a device for making the remaining prompts matter.
That is the whole logic of this chapter, and it scales up one more level.
Because there is a version of this problem that permissions alone do not solve. Sometimes the risk is not any individual command. Sometimes the risk is the shape of the change. You ask for something that sounds small, the session understands it slightly differently than you meant, and eleven files later you are reading a diff that reorganized a directory you were not thinking about. Nothing in it needed a scary permission. Every edit was inside the repo, every command was on your allow list, and the result is still not what you wanted.
The answer to that is plan mode, and it is the same idea as the permissions file moved up a layer. Instead of scoping every prompt by hand and hoping your phrasing was tight enough, you put the session into a mode where it cannot change files at all. It can read everything. It can search, open, trace the call graph, go find out how your data layer actually works. What it cannot do is edit, or write, or run a command that changes state. It reads, and then it tells you what it intends to do.
You get there with the plan mode toggle — shift and tab cycles the session's mode, and there is a startup flag for beginning a session that way. The point is that the guarantee is structural. You are not trusting the session to hold off on editing. It cannot edit. That distinction is worth something on a Tuesday afternoon when your judgment is not at its best.
When should you start in plan mode? Three cases, and they share a family resemblance. Unfamiliar code, where you do not yet know what depends on what, and a plan is the cheapest possible way to find out what the session thinks the answer is. Changes that span several files, because that is precisely where the misunderstanding compounds — one wrong assumption in file one becomes a consistent, thorough, wrong pattern in files two through seven. And anything you would want a colleague to sketch before they started. That last one is the most useful test, because you already have good instincts about it. If you would say "walk me through your approach first" to a person, say it to the session.
And when is it just friction? A one-line fix. A rename. Anything where reading the plan takes longer than reading the change itself. Plan mode has a cost, and the cost is a round trip; if the diff is three lines, skip it, look at the diff, move on. There is no virtue in ceremony.
Moving from plan to acting is a single approval. The session presents what it intends to do, you accept, and it proceeds — and now here is the warning that everyone needs and almost nobody gets in advance.
The approved plan is not a contract.
It is a statement of intent produced before the work started, and the work teaches things the plan did not know. The session may discover mid-flight that the change requires touching a file that was not on the list, and it may quite reasonably touch it. So the habit is not "approve the plan and relax." The habit is: read the file list in the plan, then read the file list in what actually changed, and compare them. Every file in the second list that was not in the first one is a question. Usually the answer is fine and obvious. Occasionally it is the most important thing you will learn that day.
Which brings us to the drawer of tools for checking on a session while it is running, because none of this configuration is any good to you if you can only inspect it by opening files.
There is a small cluster of built-in commands for looking at the current state. Typing slash permissions opens the permissions view, showing what this session is actually allowed to do right now and letting you edit the rules from inside the session. That is the one that matters most, and I will come back to it in thirty seconds. Slash config opens the settings, and it is how you see which values are genuinely in effect rather than which ones you believe you wrote. Slash context shows you how much of the context window is currently spent and what is occupying it — which is the number that quietly determines whether the session still remembers the constraint you gave it forty minutes ago. Slash status gives you the session's overall picture: version, account, working directory, model.
And there is a cluster for changing things without stopping. This is the part that saves real time. The permissions view is editable mid-session, so when a prompt appears for the fifth time and you have decided you will always say yes to it, you do not have to note it down, finish your work, edit a file, and restart. You add the rule now, from inside the session, and the interruption never happens again. Some prompts even offer to remember the answer for you when you approve them. Slash model switches models mid-session. Slash clear wipes the conversation to start clean without losing the shell you are sitting in, and slash compact squeezes the history down when the context number from slash context is getting uncomfortable. Slash help prints the whole list, which is worth doing once, today, because there are more of these than anyone remembers.
Now, the second form of today's pitfall — and it is the one that gets experienced developers, not beginners.
Configuration drift.
It goes like this. Some Tuesday, you are in a hurry, and the session keeps asking about something. You are tired of it. So you drop a broad rule into your local settings file, the uncommitted one — maybe not a bare "allow Bash," you are better than that, but something wide. Wide enough. It works. The prompts stop. You get on with your afternoon and you never think about that line again.
Three weeks later, the committed project settings file is what everyone on the team believes is running, including you. It is the one you review. It is the one you would open if someone asked. And on your machine, it has not been the effective configuration for the better part of a month, because a higher layer has been quietly overriding it since that Tuesday.
The symptom is small and easy to explain away: you stop getting prompts you expect to get. A colleague mentions the session asked them about something, and you realize it has not asked you about that in a long while. That gap — between the permissions you can describe from memory and the permissions actually in force — is the thing to watch for, and it never announces itself.
The habit that catches it costs about four seconds. At the start of a session, before the real work, type slash permissions and look. Not at the file you remember writing. At what the session says it is permitted to do right now, with every layer already resolved. If something is in there you cannot account for, you have found your drift, and you have found it while you are calm rather than at prompt thirty.
That habit generalizes, and it is the whole spirit of this chapter: trust the resolved state, not your memory of the source.
So, four moves. Write your permissions into layered files rather than deciding in the moment — user-level for your habits, the committed project file for the team, and the local file only for things genuinely specific to your machine. Build the allow list for convenience, to buy your attention back, and build the deny list as the actual safety net, short and specific and aimed at the things you cannot undo, remembering always that deny beats everything above it. Use plan mode when a change spans files or lives in code you do not know yet, and reread the file list afterward, because the plan was never a contract. And check the effective permissions at the start of a session with slash permissions, so the configuration you believe in is the configuration that is running.
None of this makes the session more powerful. It makes its power legible, which is the thing you need before you start building anything on top of it — and next we start building, with the hooks and skills and subagents that turn this configured session into something that extends.
Now, what shipped.
The headline for anyone who cares about blast radius, which after the last hour should be all of us, is a new restricted mode in the command-line tool. There is a flag for it, spelled with two dashes and the word restricted, and an environment variable that does the same job. What it does is a much blunter instrument than the permissions file we just built: it turns off command execution entirely, turns off arbitrary code running, turns off web fetching, and confines file modification strictly to the current working directory. And critically, it ignores your settings files altogether. That last part is the interesting bit. Everything we discussed today is a negotiated configuration, and this is the escape hatch that skips the negotiation — useful for the case where you want to point a session at code you do not trust and know that nothing you configured on a forgetful Tuesday can widen it. Why you care: this is the closest thing to a hard ceiling the tool has. Smallest next action: run one session on an unfamiliar repository with that flag and notice which of your habits it breaks. That tells you what you were relying on.
On the same release track, authentication got a new path. You can now log in against Console API billing directly, using the auth login command with a console flag, alongside the existing single-sign-on and email options. If you have been running one account for subscription work and juggling a separate arrangement for API-billed work, this removes a step. Smallest action: check which one you are actually authenticated as before your next billing surprise.
Two smaller CLI additions worth knowing about. There is a keybinding preference now, and it takes a readline value, which gets you the Bash-style control-W behavior for deleting a word backward. If your fingers already know readline, this is a free win. And prompt-level frontmatter can now override effort level and set an experimental cache time-to-live, choosing between the five-minute and the one-hour window — which is a per-prompt lever on a cost tradeoff, and cost is a topic we will earn properly later in this stretch.
There is also a new statusline metric that deserves attention, because it makes something visible that used to be a guess. The statusline can now expose your rolling five-hour and seven-day rate limit usage, with a used-percentage field and a resets-at field. Why you care: the dual quota — a five-hour rolling prompt window plus a weekly cap on active compute hours, shared across Claude Code, the web app and Cowork — has been in force for a while, and until now you found out where you stood by hitting the wall. Smallest action: put the used-percentage field in your statusline today. Watching that number climb changes how you sequence a long afternoon.
On the editor side, there is a native extension for VS Code with real-time progress in the sidebar and inline diffs. If you have been reading diffs in a terminal pane, inline is a genuinely better experience for exactly the review habit we talked about — comparing the plan's file list against what actually changed is much easier when the changes are laid out where you normally read code.
And from the community, on the primitives we are heading toward next: a curated collection called Everything Claude Code bundles sub-agents, skills, event-driven hooks, commands and integrations together in one repository, and you can try it either by copying pieces into your Claude directory or by pointing the tool at the cloned repo with the plugin directory flag. There is also a skills-and-methodology framework called Superpowers, aimed squarely at structured planning and testing workflows, loadable the same way. Why you care: both are readable examples of the extension layer rather than descriptions of it. Smallest action: clone one and read a single hook definition. You will recognize more of it than you expect, because a hook is just another way of deciding in advance what enter is allowed to mean.
