- Book: RAG Pocket Guide
- Also by me: Thinking in Go (2-book series) — Complete Guide to Go Programming + Hexagonal Architecture in Go
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
You ask Claude Code for the new orders page. It needs the real GET /orders route and the real OrderDto, so you point it at the backend repo next door. The page comes out right. Then git status in the backend shows a "tidied" DTO and a renamed field that nobody asked for, and that change is now waiting to break the mobile app.
That access is the documented default. Claude Code's permissions docs say files in an added directory "follow the same permission rules as the original working directory": readable without prompts, and "file editing permissions follow the current permission mode." In acceptEdits mode, the docs say file edits in the working directory or the added directories are accepted automatically. Add the backend, and it is as editable as the frontend.
What you actually want is the backend as reference material, with writes switched off. Here is that setup, step by step, with the frontend at ~/code/web and the backend at ~/code/api.
Step 1: give Claude the backend with --add-dir
Start Claude Code in the frontend and add the backend as an extra working directory. The flag is in the CLI reference:
cd ~/code/web
claude --add-dir ~/code/api
Already in a session? Run /add-dir ~/code/api instead. To keep the directory across sessions, list it under permissions.additionalDirectories in a settings file (docs).
The two routes don't load the same things. A directory added with --add-dir or /add-dir also brings in the backend's .claude/skills/, .claude/commands/ and .claude/agents/. A directory listed in additionalDirectories grants file access only. The backend's CLAUDE.md loads only when you set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 (docs). If the backend has a /deploy command of its own, the flag route puts it in your frontend session's command list.
Step 2: one deny rule that blocks Claude's edits to the backend
Create .claude/settings.local.json in the frontend repo:
{
"permissions": {
"deny": [
"Edit(~/code/api/**)"
]
}
}
That rule covers Claude's edit tools and the shell file commands Claude Code recognizes. Scripts and other subprocesses need the sandbox, which the gap section below sets up.
It's Edit, not Write. The docs say Edit rules "apply to all built-in tools that edit files," Write included. A path rule written for Write is accepted but "never consulted," and Claude Code warns at startup (docs). A deny list with Write(~/code/api/**) alone looks safe and does nothing.
The path prefix matters. In Edit and Read rules, ~/path starts at your home directory and //path is an absolute path. A single leading slash is not the filesystem root: /path anchors at the settings source, which for project and local settings means the session's primary working directory. A rule like Edit(/code/api/**) would point inside the frontend.
Deny wins. Rules are evaluated deny, then ask, then allow, and "an allow rule can't carve an exception out of a deny rule." A broad allow you approved last week won't reopen the backend.

Read access through the glass, writes stopped at it.
Why the local file? The path is specific to your machine's layout. The settings docs say Claude Code adds settings.local.json to your global git excludes when it creates the file. Create it by hand and you add it to .gitignore yourself. If the whole team keeps the two repos side by side under the same layout, put the rule in the committed .claude/settings.json. Per the docs, deny rules there apply right away, even before a teammate trusts the folder.
For a one-off session with no file at all, pass the same rule on the command line. --disallowedTools takes deny rules:
claude --add-dir ~/code/api \
--disallowedTools "Edit(~/code/api/**)"
Run /permissions inside the session to see every rule and the settings file it came from. claude doctor prints settings-file validation errors without starting a session.
Step 3: prove the deny fires, then check both repos
Don't trust a rule you haven't watched block something. First, record where the backend stands:
git -C ~/code/api branch --show-current
git -C ~/code/api status --short
git status --short prints one line per changed or untracked path. On a clean tree it prints no path lines at all, and that empty result is your baseline. If it isn't empty, commit or stash the backend first, or you won't be able to tell your changes from the agent's later.
Now give Claude something the rule must stop:
Open the OrderDto file in ~/code/api and add the
comment "// read-only test" as its first line.
Claude can find and read the file, because reads in an added directory need no prompt. The edit should be refused by Claude Code itself, with no permission prompt for you to answer, and Claude should report that it wasn't allowed. The exact wording differs between versions, so watch for the refusal itself. If you get a permission prompt instead, or the edit goes through, the rule didn't match: check the path prefix in /permissions, then undo the comment.
Then the real task:
Build src/pages/orders.tsx from the GET /orders route
and OrderDto in ~/code/api. Read the API, don't
change it. If the DTO looks wrong, tell me.
When it finishes, check both sides:
git -C ~/code/api status --short
git -C ~/code/web status --short
The backend should still print nothing. The frontend should list src/pages/orders.tsx (as ?? if it is new) and whatever else the task touched. The prompt's last line matters too: Claude can't fix the DTO, so you want it to say what looked wrong, and that note goes to the backend team.
The gap: shell commands that write files
A deny rule on Edit covers more than the edit tools. Per the permissions docs, it also applies to file commands Claude Code recognizes in Bash, such as sed and tee, and to redirection targets like > file. It does not apply to "arbitrary subprocesses that read or write files indirectly." If Claude runs npm run codegen inside the backend, or a Node script that writes files, the rule never sees the path.
For that you need the OS-level sandbox, which wraps Bash commands and every process they start. Know its default first: sandboxed commands can write to the working directory and to any directory you added with --add-dir, /add-dir or additionalDirectories. So add a write deny for the backend and switch off the unsandboxed retry:
{
"permissions": {
"deny": [
"Edit(~/code/api/**)"
]
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyWrite": ["~/code/api"]
}
}
}
The sandbox docs say paths from permission rules and sandbox settings are merged into one sandbox configuration, so the Edit deny should already cover this. The explicit denyWrite makes the intent obvious to whoever reads the file next. Sandbox paths use the usual ~/code/api form; the // and / prefixes belong to permission rules. The allowUnsandboxedCommands line closes the sandbox's escape hatch: when a command fails inside the sandbox, Claude may retry it outside, and the docs say to decline that retry. An allow rule that matches the command approves the retry with no prompt, so setting false is the safer choice (sandboxing docs). You can also turn the sandbox on with /sandbox in a session. It runs on macOS, Linux and WSL2. On native Windows, the docs say Claude Code runs commands unsandboxed.
The same setup in Hermes IDE 2.3
I build Hermes IDE, so weigh this section with that in mind. Hermes 2.3.0, released October 13, 2026, lets a Claude Code task work across more than one folder. Press ⌘N (Ctrl+Shift+N on Windows and Linux), open the project menu with ⌘P (Ctrl+P), and tick up to 5 extra folders next to your main project, or press Shift+Enter on one. The project chip shows how many you added. A line under the chips lists each extra folder with its current branch and any uncommitted changes. The screenshots below use an API repo as the main project; for this setup, pick the frontend as main and tick the backend as the extra folder.

Hermes 2.3: the project picker with a main project and two ticked extra folders, each showing its branch and uncommitted changes.
Only the main project gets the task's new hermes/ branch and worktree. Extra folders are worked on where they are, on the branch they are on, so there is one branch to review and land. Hermes also tells you when a folder can't be added, for example when it's the same repository as one you already picked, or it sits inside your main project.
Hermes does not make an extra folder read-only. The launcher notes under the chips that extra folders are edited in place, not on the task branch. The deny rule from step 2, plus the sandbox denyWrite for shell commands, is what keeps the backend read-only. By default, Hermes runs the real claude command in a terminal, so your Claude Code settings apply as usual. And the task's worktree should still pick up your local rule: the settings docs say that in a worktree, Claude Code uses .claude/settings.local.json at the main checkout's root.

The launcher with Claude Code in Accept edits mode and two extra folders. The deny rule, plus the sandbox for shell writes, is what keeps them unchanged.
The branch and uncommitted-changes line under the chips shows a dirty backend before you launch, so you can clean it up first. And since 2.3.1, the "Agent replies" strip under a session says it tracks changes in the main project only. A reply chip there won't show an edit in the backend, so the git -C ~/code/api status --short check still applies.
Hermes is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It is not affiliated with Anthropic or any other agent vendor. Download it or read the source on GitHub.
If this was useful
The pattern here is grounding: give the model the real source of truth to read, so it stops guessing at your API. The RAG Pocket Guide covers the same idea when the source is too big to hand over whole, with retrieval, chunking and reranking patterns for production. If you try the deny rule in Hermes and something behaves differently from what I described, open an issue.

Top comments (1)
good.
I wanna have meaningful conversation about collaboration with you. how about you?