<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel>
<title>Atlas blog</title><link>https://bridge-terminal.com/atlas/blog/</link><description>AI coding agents, Claude Code and Codex — what's new, in plain English and for engineers.</description><language>en</language>
<atom:link href="https://bridge-terminal.com/atlas/blog/rss.xml" rel="self" type="application/rss+xml"/>
<lastBuildDate>Thu, 15 Oct 2026 12:15:45 GMT</lastBuildDate>
<item><title>Claude Code Skills: Turn Repeated Prompts into Commands</title><link>https://bridge-terminal.com/atlas/blog/claude-code-skills-reusable-commands/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-skills-reusable-commands/</guid><pubDate>Thu, 15 Oct 2026 12:15:45 GMT</pubDate><description>Claude Code skills turn a checklist you keep pasting into a reusable /command. Covers SKILL.md, arguments, shell injection and tool permissions.</description><category>claude-code</category><category>skills</category><category>slash-commands</category><category>ai-agents</category><content:encoded><![CDATA[<ul><li>A Claude Code skill is a folder with a SKILL.md file; create one when you keep pasting the same checklist or when a CLAUDE.md section has turned into a procedure.</li><li>A skill body loads only when it is used, so long procedures cost little context compared with CLAUDE.md, which is always loaded.</li><li>Frontmatter controls who can start a skill: disable-model-invocation keeps risky actions like deploys manual, and allowed-tools pre-approves the commands you list for that turn without restricting other tools.</li><li>Shell injection with !<code>command</code> puts live data such as a git diff into the prompt before Claude sees it, but a failing command aborts the whole invocation. context: fork runs the skill in a subagent.</li><li>Old .claude/commands/ files still work; commands and skills have been merged, so new work should use the skills layout.</li></ul><p>If you keep pasting the same instructions into Claude Code, make it a skill. A skill is a directory with a <code>SKILL.md</code> file; Claude can load it when it is relevant, or you can run it yourself as <code>/skill-name</code>. This article shows how to write one, pass arguments, inject live data, limit permissions and share it with your team.</p>
<p>Everything below follows the Claude Code documentation on skills, linked at the end.</p>
<h2 id="what-is-a-claude-code-skill">What is a Claude Code skill?</h2>
<p>A skill is a Markdown file with YAML frontmatter between <code>---</code> markers, followed by instructions. The frontmatter must start on the first line of the file, or it is not read. The key property is when the body loads: unlike CLAUDE.md content, a skill&#39;s body loads only when it is used.</p>
<p>That is the whole design argument. CLAUDE.md holds facts that must always be in context: build commands, conventions, where things live. Procedures, such as &quot;how we cut a release&quot; or &quot;how we review a migration&quot;, belong in skills, where they cost little until needed. If you are deciding what goes where, see <a href="/atlas/blog/claude-md-best-practices-project-instructions/">CLAUDE.md best practices</a>.</p>
<h3 id="are-skills-the-same-as-slash-commands">Are skills the same as slash commands?</h3>
<p>Custom commands have been merged into skills. A file at <code>.claude/commands/deploy.md</code> and one at <code>.claude/skills/deploy/SKILL.md</code> both create <code>/deploy</code>, and existing command files keep working. If a skill and a command file share a name, the skill wins. Skills add a directory for supporting files, frontmatter that controls who can invoke them, and automatic loading by Claude. Command files accept the same frontmatter, except <code>name</code> and <code>paths</code>.</p>
<h2 id="where-do-skills-live">Where do skills live?</h2>
<p>Location decides who gets the skill:</p>
<div class="table"><table><thead><tr><th style="text-align:left">Location</th><th style="text-align:left">Path</th><th style="text-align:left">Scope</th></tr></thead><tbody><tr><td style="text-align:left">Enterprise</td><td style="text-align:left"><code>.claude/skills/</code> inside the managed settings directory</td><td style="text-align:left">Machines where your organization deploys it</td></tr><tr><td style="text-align:left">Personal</td><td style="text-align:left"><code>~/.claude/skills/&lt;skill-name&gt;/SKILL.md</code></td><td style="text-align:left">All your projects</td></tr><tr><td style="text-align:left">Project</td><td style="text-align:left"><code>.claude/skills/&lt;skill-name&gt;/SKILL.md</code></td><td style="text-align:left">This repository</td></tr><tr><td style="text-align:left">Nested</td><td style="text-align:left"><code>&lt;subdir&gt;/.claude/skills/&lt;skill-name&gt;/SKILL.md</code></td><td style="text-align:left">Sessions in or below that subdirectory</td></tr><tr><td style="text-align:left">Plugin</td><td style="text-align:left"><code>&lt;plugin&gt;/skills/&lt;skill-name&gt;/SKILL.md</code></td><td style="text-align:left">Where the plugin is enabled, invoked as <code>/plugin-name:skill-name</code></td></tr></tbody></table></div>
<p>When names collide, enterprise beats personal, and personal beats project. Commit project skills to the repository and the whole team gets them. Nested skills load the first time Claude reads or edits a file in that subdirectory, which suits monorepos.</p>
<h2 id="your-first-skill-summarize-and-review-a-diff">Your first skill: summarize and review a diff</h2>
<p>Create <code>.claude/skills/diff-review/SKILL.md</code>:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
allowed-tools: Bash(git diff *)
---

## Current changes

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.</code></pre></div>
<p>Two things happen here. The <code>description</code> tells Claude when to load the skill, so write it as &quot;what it does, plus when to use it&quot;. The combined <code>description</code> and <code>when_to_use</code> text is truncated at 1,536 characters in the skill listing, so put the key use case first.</p>
<p>The line starting with <code>!</code> followed by a backtick-wrapped command is shell injection. Claude Code runs <code>git diff HEAD</code> before Claude sees the skill and replaces the placeholder with the output. Claude sees the real diff, not an instruction to go and fetch it. The inline form is recognized only at the start of a line or after whitespace. Edits to existing skill files take effect in the current session without a restart; if you create a brand-new top-level skills directory mid-session, run <code>/reload-skills</code>.</p>
<p>Two rules from the docs matter in practice. First, if an injected command fails, the whole invocation aborts and Claude never sees the skill. Any non-zero exit counts, apart from exit code 1 from search and comparison commands, so append <code>|| true</code> to commands that may fail. Second, injected commands are checked against your permission rules and do not prompt. A deny match aborts, and outside auto mode anything other than an allow also aborts, which is why the example pre-approves the command with <code>allowed-tools</code>. For multi-line commands, use a fenced block opened with three backticks and a <code>!</code>.</p>
<h2 id="how-do-i-pass-arguments-to-a-skill">How do I pass arguments to a skill?</h2>
<p><code>$ARGUMENTS</code> expands to everything after the skill name:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---

Fix GitHub issue $ARGUMENTS following our coding standards.</code></pre></div>
<p>Running <code>/fix-issue 123</code> makes Claude receive &quot;Fix GitHub issue 123 following our coding standards.&quot; For several values, use <code>$ARGUMENTS[N]</code> or the shorthand <code>$0</code>, <code>$1</code>, <code>$2</code> (indexes start at 0):</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
name: migrate-component
description: Migrate a component from one language to another
---

Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.</code></pre></div>
<p>Quote multi-word values: <code>/my-skill &quot;hello world&quot; second</code> makes <code>$0</code> equal <code>hello world</code>. If the skill text contains no placeholder, Claude Code appends <code>ARGUMENTS: &lt;value&gt;</code> to it instead.</p>
<p>You can also declare named positional arguments in an <code>arguments</code> field and use <code>$issue</code> or <code>$branch</code> in the body, and add <code>argument-hint</code> (for example <code>[issue-number]</code>) for autocomplete. Other substitutions include <code>${CLAUDE_SKILL_DIR}</code>, the directory containing <code>SKILL.md</code>, which is useful for pointing at a bundled script. To write a literal dollar sign before a digit, escape it as <code>\$1.00</code>.</p>
<h2 id="who-can-run-a-skill-you-claude-or-both">Who can run a skill: you, Claude, or both?</h2>
<p>Two frontmatter fields control invocation:</p>
<div class="table"><table><thead><tr><th style="text-align:left">Frontmatter</th><th style="text-align:left">You can invoke</th><th style="text-align:left">Claude can invoke</th></tr></thead><tbody><tr><td style="text-align:left">(default)</td><td style="text-align:left">Yes</td><td style="text-align:left">Yes</td></tr><tr><td style="text-align:left"><code>disable-model-invocation: true</code></td><td style="text-align:left">Yes</td><td style="text-align:left">No</td></tr><tr><td style="text-align:left"><code>user-invocable: false</code></td><td style="text-align:left">No</td><td style="text-align:left">Yes</td></tr></tbody></table></div>
<p>Use <code>disable-model-invocation: true</code> for anything with side effects: deploys, commits, messages sent outside. You do not want the model deciding on its own that now is a good time to deploy. Use <code>user-invocable: false</code> for background knowledge Claude should pull in on its own but that makes no sense as a command; it hides the skill from the <code>/</code> menu.</p>
<p>You can also restrict skills through permission rules. Deny the Skill tool in <code>/permissions</code> to disable all of them, or use rules such as <code>Skill(commit)</code> for an exact match and <code>Skill(deploy *)</code> for a prefix match to allow or deny specific ones.</p>
<h2 id="how-do-i-pre-approve-tools-for-a-skill">How do I pre-approve tools for a skill?</h2>
<p><code>allowed-tools</code> lets Claude use the listed tools without prompting during the turn that invokes the skill:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
name: commit
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---</code></pre></div>
<p>Note the limits stated in the docs: it does not restrict other tools, which still follow your normal permission settings, and the grant clears when you send your next message. The value can be space- or comma-separated, or a YAML list. If you want to remove tools instead, <code>disallowed-tools</code> takes them out of Claude&#39;s pool while the skill is active. For unattended runs, read <a href="/atlas/blog/claude-code-headless-ci-permissions/">Claude Code headless in CI</a> before relying on any of this.</p>
<h2 id="how-do-i-keep-a-skill-from-bloating-context">How do I keep a skill from bloating context?</h2>
<p>Keep <code>SKILL.md</code> under 500 lines and move detail into supporting files that Claude loads when needed:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">my-skill/
├── SKILL.md (required - overview and navigation)
├── reference.md (detailed API docs - loaded when needed)
├── examples.md (usage examples - loaded when needed)
└── scripts/
    └── helper.py (utility script - executed, not loaded)</code></pre></div>
<p>Reference the files from <code>SKILL.md</code> so Claude knows they exist. A script that is executed rather than read costs no context for its source, which makes scripts a good home for deterministic steps such as formatting a report.</p>
<h2 id="running-a-skill-in-a-subagent">Running a skill in a subagent</h2>
<p>Add <code>context: fork</code> and the skill content becomes the prompt for a new subagent. That subagent does not see your conversation history, so the instructions must be self-contained and contain an explicit task; guidelines alone produce no output:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

## Your task
Summarize this pull request...</code></pre></div>
<p>The <code>agent</code> field picks the subagent type: <code>Explore</code>, <code>Plan</code>, <code>general-purpose</code> (the default) or a custom agent. Forked skills run in the background by default, so you can keep working; set <code>background: false</code> to wait for the result. In <code>-p</code> and SDK mode the result is waited for anyway. Forking keeps noisy exploration out of your main context. For when splitting work is worth it, see <a href="/atlas/blog/claude-code-subagents-when-to-split-work/">Claude Code subagents</a>. Skills can also set <code>model</code> and <code>effort</code> overrides while active, register <code>hooks</code>, and limit automatic activation with <code>paths</code> globs.</p>
<h2 id="a-practical-checklist">A practical checklist</h2>
<ul>
<li>Start from a real repetition: the third time you paste the same prompt.</li>
<li>Write the description for the model: what it does, and when to use it.</li>
<li>Put live facts in with <code>!</code> injection instead of asking Claude to run commands, and guard commands that may exit non-zero.</li>
<li>Mark side-effecting skills <code>disable-model-invocation: true</code> and pre-approve only the exact commands needed.</li>
<li>Field names must match exactly. Claude Code ignores unrecognized fields without an error, so a typo like <code>allowed_tools</code> silently does nothing. Malformed YAML loads the skill with no fields set. Test the skill after writing it.</li>
<li>Commit it to <code>.claude/skills/</code> and review changes like code.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Skills are the right home for procedures: loaded on demand, shareable through the repository, and controllable down to who may start them and which commands run without a prompt. Keep CLAUDE.md for the facts that always apply, and move every recurring checklist into a skill.</p>
<p><a href="/atlas/">Atlas</a> is a team of AI engineers built on Claude Code, where each engineer has a written role. Whether Atlas lets you attach your own skills to those roles is covered below.</p>
]]></content:encoded></item>
<item><title>How to Check AI-Built Software When You Don't Write Code</title><link>https://bridge-terminal.com/atlas/blog/check-ai-built-software-without-code/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/check-ai-built-software-without-code/</guid><pubDate>Thu, 15 Oct 2026 08:00:00 GMT</pubDate><description>You can't read the code, but you can still tell whether an AI agent's work is good. Ask for evidence, set pass/fail checks, and try the edge cases yourself.</description><category>ai-for-founders</category><category>ai-agents</category><category>claude-code</category><category>software-testing</category><content:encoded><![CDATA[<ul><li>You can judge AI-built software without reading code. Ask for evidence such as test results and screenshots instead of accepting a message that says the work is done.</li><li>Anthropic's guide for Claude Code says the agent stops when the work looks done, so write down beforehand what done means, as checks that clearly pass or fail.</li><li>Have the work checked by a reviewer that did not write it, and ask it to flag only problems that affect correctness or your stated requirements.</li><li>Try the awkward cases yourself: wrong inputs, empty fields, a logged-out user, a slow connection. Work that looks finished but isn't usually breaks there.</li><li>If nobody can show you that a change works, it is not ready to release, however confident the agent sounds.</li></ul><p>You can check AI-built software without reading a line of code. Instead of judging the code, judge the evidence: what was tested, what the result was, and what the screen looks like now. Settle what &quot;done&quot; means before the work starts and ask for proof afterwards. That is usually enough to tell good work from work that only looks finished.</p>
<p>This guide is for founders, product owners, analysts, testers and managers who brief AI coding agents such as Claude Code or OpenAI Codex, or who oversee people who do.</p>
<h2 id="why-its-done-is-not-enough">Why &quot;it&#39;s done&quot; is not enough</h2>
<p>An AI coding agent is a program that reads a project&#39;s files, makes changes and runs commands to finish a task you describe. It can work for long stretches on its own, and it has one habit you need to understand.</p>
<p>Anthropic&#39;s best-practice guide for Claude Code says it directly: &quot;Claude stops when the work looks done.&quot; Without a check the agent can run, the guide goes on, &quot;looks done&quot; is the only signal available, and &quot;you become the verification loop: every mistake waits for you to notice it.&quot;</p>
<p>The same guide lists a common failure it calls the trust-then-verify gap: the agent &quot;produces a plausible-looking implementation that doesn&#39;t handle edge cases.&quot; An edge case is an unusual situation, such as an empty field or a very long name. The guide&#39;s advice is short: &quot;If you can&#39;t verify it, don&#39;t ship it.&quot;</p>
<p>So if you don&#39;t code, don&#39;t ask &quot;is this code good?&quot; Ask &quot;what proof do I have that this does what I asked?&quot;</p>
<h2 id="step-1-define-done-before-the-work-starts">Step 1: Define &quot;done&quot; before the work starts</h2>
<p>The best checks are written before anything is built. In the brief, describe the result you will accept, not only the feature.</p>
<p>A vague brief: &quot;Add a discount code box to checkout.&quot;</p>
<p>A brief you can check:</p>
<ul>
<li>A valid code takes 10% off the total, and the new total shows before payment.</li>
<li>An expired code shows &quot;This code has expired&quot; and changes nothing.</li>
<li>A made-up code shows &quot;Code not recognised&quot;.</li>
<li>Using the same code twice in one order is refused.</li>
<li>Before and after screenshots of the checkout page come with the work.</li>
</ul>
<p>Each line either happens or doesn&#39;t, and that is what makes it useful. Anthropic&#39;s guide recommends giving the agent &quot;something that produces a pass or fail&quot;: example inputs with the answers you expect, for instance, or a screenshot to compare against a design. With a check like that, the agent can run it itself and keep going until it passes, instead of waiting for you to find the problem.</p>
<p>For bigger features, the guide suggests letting the agent interview you first, asking about edge cases and trade-offs, and then write a spec (a written description of what will be built). The most useful specs, it says, state what is out of scope and &quot;end with an end-to-end verification step that proves the feature works.&quot; You don&#39;t need technical knowledge for this. You only answer questions about how the business should behave.</p>
<h2 id="step-2-ask-for-evidence-not-a-summary">Step 2: Ask for evidence, not a summary</h2>
<p>When the agent reports back, a confident summary is not proof. Ask for the things that are.</p>
<p>Anthropic&#39;s guide recommends having the agent &quot;show evidence rather than asserting success: the test output, the command it ran and what it returned, or a screenshot of the result.&quot; It notes that reviewing evidence is faster than running the check again yourself, and &quot;it works for sessions you weren&#39;t watching.&quot;</p>
<p>What to ask for, in plain words:</p>
<ul>
<li><strong>Test results.</strong> A test is a small automatic check that runs the software and compares what happens with what should happen. Ask which tests were run, how many passed, whether any failed and whether any were skipped.</li>
<li><strong>Screenshots.</strong> For anything on screen, ask for screenshots of the finished result. Include error messages and empty screens, not only the case where everything goes right.</li>
<li><strong>A list of what changed.</strong> Ask which parts of the product were touched. If you asked for a discount box and the list includes the sign-in page, ask why.</li>
<li><strong>What was not done.</strong> Ask directly: &quot;What did you leave out, and what are you unsure about?&quot;</li>
</ul>
<h3 id="warning-signs-in-a-report">Warning signs in a report</h3>
<ul>
<li>&quot;Should work now&quot;, with no test results.</li>
<li>Tests were switched off, skipped or deleted so that everything passes.</li>
<li>An error was hidden instead of fixed. One of the guide&#39;s example instructions tells the agent to &quot;address the root cause, don&#39;t suppress the error.&quot;</li>
<li>Changes far outside what you asked for.</li>
</ul>
<h2 id="step-3-get-a-second-opinion-from-something-that-did-not-writ">Step 3: Get a second opinion from something that did not write the code</h2>
<p>People find it hard to review their own work, and agents do too. Anthropic&#39;s guide notes that in a fresh session (a new conversation that has not seen the earlier work) Claude &quot;won&#39;t be biased toward code it just wrote.&quot; It suggests a reviewer that &quot;sees only the diff and the criteria you give it.&quot; A diff is the list of exact changes.</p>
<p>You don&#39;t have to run the review yourself. You only need to make sure one happens and read its verdict. The guide adds a caution: a reviewer told to find gaps &quot;will usually report some, even when the work is sound.&quot; If you act on every comment, you end up with over-engineering: extra layers, defensive code and tests for cases that can&#39;t happen. Tell the reviewer to flag only problems that affect correctness or your stated requirements, and treat the rest as optional.</p>
<h2 id="step-4-try-it-yourself-the-awkward-way">Step 4: Try it yourself, the awkward way</h2>
<p>Testers already do this, and everyone else can too. Use the feature the way a careless, impatient or unlucky customer would:</p>
<ul>
<li>Type the wrong thing: letters in a phone field, a date in the past, a very long name.</li>
<li>Leave fields empty and press submit.</li>
<li>Use it logged out, or as a user with fewer permissions.</li>
<li>Press the button twice quickly.</li>
<li>Try it on a phone and on a slow connection.</li>
<li>Go back, refresh, and check whether your data is still there.</li>
</ul>
<p>These are the edge cases where a &quot;plausible-looking implementation&quot; tends to fail. When you find a problem, report it the way the guide suggests for bugs: the symptom, where it likely happens, and what &quot;fixed&quot; looks like. &quot;After the session times out, logging in fails; fixed means I can log in again without clearing my browser&quot; is more useful than &quot;login is broken.&quot;</p>
<h2 id="how-do-i-judge-quality-beyond-does-it-work">How do I judge quality beyond &quot;does it work&quot;?</h2>
<p>Working is the minimum. A few more questions, all answerable without code:</p>
<ul>
<li><strong>Security.</strong> Did anything touching sign-in, payments or personal data get its own security check? Ask what was checked, not only whether it was.</li>
<li><strong>Speed.</strong> Is there a target (&quot;search results in under a second&quot;), and was it measured?</li>
<li><strong>Undoing a release.</strong> If this release goes wrong, how do we go back, and has that been tried? Was the data backed up first?</li>
<li><strong>Consistency.</strong> Does the new screen look and behave like the rest of the product?</li>
</ul>
<p>If nobody can answer one of these, you have found a gap.</p>
<h2 id="what-your-role-becomes">What your role becomes</h2>
<p>When AI agents do the building, a non-technical owner stops checking the work line by line. The job becomes setting the standard and checking the proof. In practice that means three things:</p>
<ol>
<li>Write briefs with pass/fail acceptance criteria.</li>
<li>Don&#39;t accept &quot;done&quot; without evidence.</li>
<li>Keep the decisions that carry real risk, such as go-live, spending and deleting data, in human hands.</li>
</ol>
<p>Some tools build these habits into the process. <a href="/atlas/">Atlas</a> is a software team of AI engineers built on Claude Code. It won&#39;t let work be marked done until a screen has its test cases and screenshots. Each build is reviewed when it is finished and is either marked releasable or gets fix tasks sent back to the engineer who built it. Go-lives, spending and deleting data are never decided for you. Whatever tool you use, the habits above still apply.</p>
<h2 id="summary">Summary</h2>
<p>You don&#39;t need to read code to hold AI-built software to a high standard. Decide what &quot;done&quot; means before the work starts. Ask for test results and screenshots instead of reassurance. Make sure something other than the author reviews the work. Then try the awkward cases yourself. If nobody can show you that a change works, it isn&#39;t ready, however good the summary sounds.</p>
]]></content:encoded></item>
<item><title>Claude Haiku 5.5: What the Cheapest Claude Means for Teams</title><link>https://bridge-terminal.com/atlas/blog/claude-haiku-5-5-what-it-means-for-teams/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-haiku-5-5-what-it-means-for-teams/</guid><pubDate>Thu, 15 Oct 2026 02:00:00 GMT</pubDate><description>Claude Haiku 5.5 arrived on 7 October 2026 with a 1M-token window at $0.10 per million input tokens. What it is good for, what it is not, and how to test it.</description><category>claude</category><category>claude-code</category><category>ai-models</category><category>ai-costs</category><category>ai-for-founders</category><content:encoded><![CDATA[<ul><li>Anthropic released Claude Haiku 5.5 on 7 October 2026. It is the fastest and cheapest model in its current line-up: $0.10 per million input tokens and $0.50 per million output tokens for prompts up to 100,000 tokens.</li><li>Haiku 5.5 can read up to 1 million tokens at once and write up to 128,000. Once a prompt passes 100,000 tokens, every token in that request, output included, costs five times as much.</li><li>Anthropic suggests Haiku 5.5 for high-volume work whose results can be checked, such as sorting, extraction, routing and helper tasks, while its model guide says most workloads start with Opus 5.5.</li><li>On the GPQA Diamond reasoning test Anthropic reports 85% for Haiku 5.5 and 91% for Opus 5.5 and Sonnet 5.5, with Haiku's cost per correct answer about a twentieth of Opus 5.5's.</li><li>Anthropic calls its figures internal and directional, not guarantees. Test Haiku 5.5 on your own tasks before moving work to it, and check again whenever a new model comes out.</li></ul><p>Claude Haiku 5.5 is Anthropic&#39;s new small model, released on 7 October 2026. It is the fastest and cheapest model in Anthropic&#39;s current line-up. It costs $0.10 for every million tokens it reads and $0.50 for every million it writes, as long as the prompt stays under 100,000 tokens (a token is a small piece of a word). It can read about 555,000 words at once. It will not replace the bigger models for hard building work, but it makes a lot of routine work much cheaper.</p>
<p>This article covers what Claude Haiku 5.5 is, what changed since the last Haiku, and what it means for a team that builds software with AI coding agents.</p>
<h2 id="what-is-claude-haiku-5-5">What is Claude Haiku 5.5?</h2>
<p>Anthropic&#39;s <a href="https://platform.claude.com/docs/en/about-claude/models/overview" rel="noopener">models overview</a> lists four current models: Fable 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5. Haiku 5.5 is listed as the fastest of the four and has the lowest price. Anthropic describes it as the model &quot;for high-volume, latency-sensitive tasks such as classification, extraction, and routing&quot;. In plain words, it is for jobs you run thousands of times where a quick answer matters: sorting incoming tickets, pulling fields out of documents, or deciding which specialist a request should go to.</p>
<p>Anthropic&#39;s <a href="https://platform.claude.com/docs/en/models/haiku-5-5/overview" rel="noopener">Haiku 5.5 page</a> adds a fourth use that matters to coding teams: &quot;subagent tasks&quot;. A subagent is a helper that a main AI agent sends off to do one narrow job, such as searching the code or summarising a log, before it reports back.</p>
<p>From launch, the model was available through Anthropic&#39;s own API (the service developers call from their code) and on Amazon Bedrock, Google Cloud, Microsoft Foundry and Claude Platform on AWS. Anthropic says it will not retire the model before 7 October 2027.</p>
<h3 id="does-claude-code-use-haiku-5-5">Does Claude Code use Haiku 5.5?</h3>
<p>Yes. The <a href="https://github.com/anthropics/claude-code/releases/tag/v2.1.293" rel="noopener">release notes for Claude Code version 2.1.293</a>, published on 7 October, say Haiku 5.5 is &quot;now the default Haiku model on the Anthropic API&quot;. So if you use Claude Code through Anthropic&#39;s API, the work it hands to its Haiku model now goes to 5.5 once you are on that version or later.</p>
<h2 id="what-changed-from-haiku-4-5">What changed from Haiku 4.5?</h2>
<p>Anthropic&#39;s <a href="https://platform.claude.com/docs/en/models/haiku-5-5/whats-new-haiku-5-5" rel="noopener">what&#39;s new page</a> lists the main differences:</p>
<ul>
<li><strong>Much more room to read.</strong> The context window (how much text the model can hold in mind at once) grows from 200,000 tokens to 1 million.</li>
<li><strong>Longer answers.</strong> One reply can be up to 128,000 tokens long, up from 64,000.</li>
<li><strong>It decides how much to think.</strong> Haiku 5.5 now chooses for itself how much to reason before it answers. A setting called &quot;effort&quot; lets developers turn that up or down, trading quality against speed and cost. The default is medium.</li>
<li><strong>The same text counts as more tokens.</strong> Haiku 5.5 splits text into tokens in a newer way, so the same text comes out about 30% more tokens than on Haiku 4.5. You pay per token, so compare total bills, not only the price per token.</li>
<li><strong>More tokens in long conversations.</strong> In a back-and-forth conversation, the model&#39;s reasoning from all earlier turns now stays in its memory and is charged as input. Haiku 4.5 kept only the latest turn&#39;s reasoning.</li>
<li><strong>Cheaper repeated instructions.</strong> Prompt caching lets you pay much less when the same opening text is sent again. It now works on prompts as short as 512 tokens, down from 4,096.</li>
</ul>
<p>Some changes also break existing code. Haiku 5.5 returns errors for some settings that older programs send, such as the ones that controlled how random the answers were, and for the older way of setting a fixed thinking budget. It also no longer accepts a common way of steering a reply, which was writing the first words of the answer for the model. If your product already calls Haiku 4.5, moving to 5.5 takes some developer work. Changing the model name is not enough.</p>
<h2 id="how-much-does-haiku-5-5-cost">How much does Haiku 5.5 cost?</h2>
<p>Prices from Anthropic&#39;s <a href="https://platform.claude.com/docs/en/models/haiku-5-5/overview" rel="noopener">Haiku 5.5 page</a>, in US dollars per million tokens:</p>
<ul>
<li><strong>Prompts up to 100,000 tokens:</strong> $0.10 to read, $0.50 to write.</li>
<li><strong>Prompts over 100,000 tokens:</strong> $0.50 to read, $2.50 to write, five times as much.</li>
<li><strong>Re-reading cached text:</strong> $0.01 (or $0.05 for prompts over 100,000 tokens).</li>
<li><strong>Batch jobs</strong> (work you can wait for instead of getting it back at once): 50% off.</li>
</ul>
<p>For comparison, the <a href="https://platform.claude.com/docs/en/about-claude/models/overview" rel="noopener">models overview</a> lists Sonnet 5.5 at $2 to read and $10 to write, and Opus 5.5 at $4 and $20. For short prompts, Haiku 5.5 reads text at a twentieth of Sonnet&#39;s price and a fortieth of Opus&#39;s.</p>
<p>The catch is the 100,000-token line. The 1-million-token window is useful, but once a prompt goes over that line, every token in that request costs five times the base rate, including the reply. Cached text counts toward the line too. A job that loads a whole codebase into every request will cost much more than the headline price suggests. Anthropic&#39;s <a href="https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence" rel="noopener">cost guide</a> suggests shortening a long session when one task ends, before its requests pass 100,000 tokens.</p>
<h2 id="is-haiku-5-5-good-enough-for-coding">Is Haiku 5.5 good enough for coding?</h2>
<p>That depends on the job. Anthropic&#39;s <a href="https://platform.claude.com/docs/en/about-claude/models/choosing-a-model" rel="noopener">guide to choosing a model</a> says &quot;most workloads start with Claude Opus 5.5&quot;. It points to Opus for &quot;multihour autonomous coding agents&quot; and to Sonnet 5.5 for everyday coding. Haiku 5.5 is listed for &quot;the lowest latency and price&quot;, including &quot;sub-agent tasks&quot;. The same guide also describes a cheaper starting route: begin with Haiku 5.5, test it, and move up only where it falls short. It suggests that route for prototypes, cost-sensitive products and high-volume, straightforward tasks.</p>
<p>The cost guide gives one direct comparison. On GPQA Diamond, a hard science-reasoning test, Haiku 5.5 scored 85% at its default setting, against 91% for both Opus 5.5 and Sonnet 5.5. (Anthropic notes that Opus and Sonnet tied only because refusals counted as wrong answers.) Haiku&#39;s cost per correct answer was &quot;about a twentieth of Claude Opus 5.5&#39;s cost per correct answer and about a tenth of Claude Sonnet 5.5&#39;s&quot;. The guide says Haiku 5.5 &quot;suits high-volume and latency-sensitive work with checkable outputs&quot;. It calls its figures internal and &quot;directional, not guarantees&quot;, and tells readers to measure on their own work.</p>
<p>&quot;Checkable&quot; is the important word. If a wrong answer gets caught because a test fails, a format check rejects it or a reviewer spots it, a cheaper model that is usually right can save a lot. If wrong answers slip through unnoticed, the savings can turn into rework.</p>
<h2 id="what-does-this-mean-for-a-team-building-software">What does this mean for a team building software?</h2>
<p>One option is to mix models. Anthropic&#39;s cost guide describes two ways to do that:</p>
<ol>
<li><strong>A big model in charge, smaller models doing the bulk work.</strong> The capable model plans and checks, and cheaper models handle many separate pieces. In Anthropic&#39;s measurements, which used an earlier Sonnet model as the helpers rather than Haiku 5.5, this saved money in only two cases: routine work where a few pieces cost far more than the rest, and work too big for one model to read at once. Otherwise, the main model on its own at a lower effort setting came out ahead.</li>
<li><strong>A small model doing the work and asking a big model for advice.</strong> The cheaper model runs the task and asks a stronger &quot;advisor&quot; model only at hard moments. This helps only if the small model actually asks. In one of Anthropic&#39;s tests, Haiku 5.5 paired with an Opus 5.5 advisor never asked once across 198 questions. The pair scored about the same as Haiku alone and cost 12% more per question.</li>
</ol>
<p>Before trying either approach, Anthropic suggests a simpler step: turn down the effort setting on the model you already use. Its model guide says &quot;tuning effort is often a better lever than switching models&quot;.</p>
<p>For a founder or manager, these are the questions to ask your team:</p>
<ul>
<li><strong>Which of our AI tasks are high-volume and easy to check?</strong> Likely candidates are ticket triage, summarising test logs, tagging data and first-pass searches through code. Architecture design and fixing subtle bugs are not.</li>
<li><strong>Have we tested it on our own work?</strong> Anthropic suggests taking a few real tasks from your own logs, writing a check for each that shows whether it succeeded, and recording the cost of each task next to its score. Run the same set on your current model and on Haiku 5.5, and compare cost per finished task as well as price per token.</li>
<li><strong>Are we filling the big window?</strong> If prompts often go over 100,000 tokens, the higher rate applies. Cutting down what goes into each request may save more than changing models.</li>
<li><strong>Who updates the code?</strong> If your product calls Haiku 4.5 today, set aside some developer time for the breaking changes.</li>
<li><strong>Will we check again?</strong> Anthropic says to &quot;recheck at every model release&quot;, because each new model changes both the quality gap and the price gap.</li>
</ul>
<h2 id="how-atlas-relates-to-this">How Atlas relates to this</h2>
<p><a href="/atlas/">Atlas</a> runs each AI engineer as its own Claude Code session, with the model and effort chosen to suit that engineer&#39;s role. That follows the same idea: not every job needs the biggest model. Atlas&#39;s published material does not say that any role uses Haiku 5.5, or that Atlas tests a new model against a role&#39;s past results before switching. In Atlas or anywhere else, treat a model change as something to measure first.</p>
<h2 id="the-bottom-line">The bottom line</h2>
<p>Claude Haiku 5.5 makes fast, simple, repeated AI work much cheaper, and it can read far more at once than the last Haiku. It does not replace Opus or Sonnet for the hard parts of building software, and long prompts cost five times as much. Anthropic&#39;s advice is to find the high-volume jobs where mistakes get caught, test Haiku 5.5 on those jobs with your own work before moving anything over, and check again when the next model comes out.</p>
]]></content:encoded></item>
<item><title>MCP Servers in Claude Code: Connect Databases and Tools</title><link>https://bridge-terminal.com/atlas/blog/mcp-servers-claude-code-explained/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/mcp-servers-claude-code-explained/</guid><pubDate>Wed, 14 Oct 2026 08:40:00 GMT</pubDate><description>How MCP servers work in Claude Code: add local and remote servers, choose a scope, handle OAuth and secrets, limit context use and lock them down for a team.</description><category>claude-code</category><category>mcp</category><category>ai-agents</category><content:encoded><![CDATA[<ul><li>An MCP server is a connector that lets Claude Code reach things outside your project, such as a database, an issue tracker or an internal API, and call its tools like built-in ones.</li><li>You add a server with <code>claude mcp add</code> and pick a scope: local (default, just you, this project), user (just you, all projects) or project (shared with the team through a committed <code>.mcp.json</code>).</li><li>Never commit real tokens in <code>.mcp.json</code>. Use <code>${VAR}</code> or <code>${VAR:-default}</code> placeholders, which Claude Code expands in <code>command</code>, <code>args</code>, <code>env</code>, <code>url</code> and <code>headers</code>.</li><li>Tool search is on by default on supported models: only tool names and server instructions load at session start, so adding servers has minimal impact on the context window.</li><li>Treat each MCP server like a new dependency. Project servers load without asking in <code>claude -p</code>, Agent SDK and cloud sessions, and admins can restrict servers with <code>managed-mcp.json</code> or allowlists and denylists.</li></ul><p>MCP servers give an AI coding agent access to things outside your repository, such as a database, an issue tracker or an internal API. In Claude Code you add one with <code>claude mcp add</code> and choose whether it is private to you or shared with the team through a <code>.mcp.json</code> file. Claude can then call that server&#39;s tools the same way it calls its built-in ones. This guide covers how that works, the commands you need, and the settings that matter once you have more than one or two servers.</p>
<h2 id="what-an-mcp-server-actually-is">What an MCP server actually is</h2>
<p>The Model Context Protocol (MCP) is described by its maintainers as &quot;an open-source standard for connecting AI applications to external systems.&quot; An application that supports MCP can connect to data sources (local files, databases), tools (search engines, calculators) and workflows (specialized prompts).</p>
<p>A server can expose three kinds of things to Claude Code:</p>
<ul>
<li><strong>Tools</strong>: functions Claude can call, such as &quot;run this query&quot; or &quot;create an issue&quot;. In Claude Code their full names look like <code>mcp__&lt;server&gt;__&lt;tool&gt;</code>.</li>
<li><strong>Resources</strong>: data you pull into a prompt with an <code>@</code> mention, for example <code>@github:issue://123</code> or <code>@postgres:schema://users</code>.</li>
<li><strong>Prompts</strong>: workflows that appear as commands. Claude Code lists them as <code>/servername:promptname (MCP)</code>, and you can also type the long form, for example <code>/mcp__github__pr_review 456</code>.</li>
</ul>
<p>Because the protocol is open, one server can work with many clients. The MCP project lists Claude, ChatGPT, Visual Studio Code and Cursor among the clients that support it. Each client has its own configuration format, so the commands below apply only to Claude Code.</p>
<h2 id="remote-and-local-servers">Remote and local servers</h2>
<p><strong>Remote HTTP servers</strong> run elsewhere and you reach them by URL. Claude Code&#39;s documentation recommends HTTP for remote servers:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp add --transport http notion https://mcp.notion.com/mcp</code></pre></div>
<p>If the server takes a static token, pass it as a header:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp add --transport http secure-api https://api.example.com/mcp \
  --header &quot;Authorization: Bearer your-token&quot;</code></pre></div>
<p>The older SSE transport is deprecated. Claude Code tries HTTP first and switches to SSE when a server does not accept it; that automatic switch needs v2.1.265 or later. On older versions, pass <code>--transport sse</code> for SSE-only servers.</p>
<p><strong>Local stdio servers</strong> are programs Claude Code starts on your machine. The <code>--</code> separates Claude Code&#39;s own options (<code>--transport</code>, <code>--env</code>, <code>--scope</code>) from the command that runs the server, and everything after it is passed to the server untouched:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
  -- npx -y airtable-mcp-server</code></pre></div>
<p>Claude Code sets <code>CLAUDE_PROJECT_DIR</code> in a stdio server&#39;s environment to the project root, so a local server can tell which project it is working in.</p>
<p>For browsers, Claude Code also has a built-in Claude in Chrome server, which admin controls treat separately (see the security section below).</p>
<h2 id="scopes-who-gets-the-server">Scopes: who gets the server</h2>
<p>Every server has a scope, set with <code>--scope</code>. It decides who sees the server and where it is stored:</p>
<div class="table"><table><thead><tr><th>Scope</th><th>Loads in</th><th>Shared with team</th><th>Stored in</th></tr></thead><tbody><tr><td><code>local</code> (default)</td><td>Current project only</td><td>No</td><td><code>~/.claude.json</code></td></tr><tr><td><code>project</code></td><td>Current project only</td><td>Yes, via version control</td><td><code>.mcp.json</code> in the project root</td></tr><tr><td><code>user</code></td><td>All your projects</td><td>No</td><td><code>~/.claude.json</code></td></tr></tbody></table></div>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash"># Shared with everyone who clones the repo
claude mcp add --transport http shared-server --scope project https://example.com/mcp

# Available in all your projects
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic</code></pre></div>
<p>If the same name is defined in more than one place, precedence is local, then project, then user, then plugin-provided servers, then claude.ai connectors. A server an organization provides through the <code>managedMcpServers</code> managed setting ranks above all of these. The winning entry is used whole; fields are not merged across scopes.</p>
<p>A sensible split: <code>project</code> for servers the whole team needs on this codebase, <code>user</code> for personal tools you want everywhere, and <code>local</code> for experiments or anything holding your own credentials.</p>
<h2 id="the-mcp-json-file-and-keeping-secrets-out-of-git">The .mcp.json file and keeping secrets out of git</h2>
<p>A project-scoped server is written to <code>.mcp.json</code>, which you commit:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;mcpServers&quot;: {
    &quot;shared-server&quot;: {
      &quot;type&quot;: &quot;http&quot;,
      &quot;url&quot;: &quot;https://example.com/mcp&quot;
    }
  }
}</code></pre></div>
<p>Do not commit real tokens here. Use environment variable expansion: <code>${VAR}</code> reads a variable, and <code>${VAR:-default}</code> falls back to a default when it is unset. Expansion works in <code>command</code>, <code>args</code>, <code>env</code>, <code>url</code> and <code>headers</code>:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;mcpServers&quot;: {
    &quot;api-server&quot;: {
      &quot;type&quot;: &quot;http&quot;,
      &quot;url&quot;: &quot;${API_BASE_URL:-https://api.example.com}/mcp&quot;,
      &quot;headers&quot;: {
        &quot;Authorization&quot;: &quot;Bearer ${API_KEY}&quot;
      }
    }
  }
}</code></pre></div>
<p>Two details catch people out:</p>
<ul>
<li>An unset variable with no default is left as the literal <code>${VAR}</code> text. The config still loads, and Claude Code reports a missing-variable warning.</li>
<li>In a remote server&#39;s <code>url</code> and <code>headers</code>, certain credential variables always read as empty, and a <code>:-default</code> on them is ignored. Examples include <code>ANTHROPIC_API_KEY</code>, <code>ANTHROPIC_AUTH_TOKEN</code>, <code>AWS_BEARER_TOKEN_BEDROCK</code>, <code>HTTPS_PROXY</code> and <code>NPM_TOKEN</code>. If a server needs one of those values, copy it into a variable with your own name.</li>
</ul>
<p>For short-lived tokens or SSO, <code>headersHelper</code> runs a command that prints a JSON object of headers. It runs fresh on each connection, is not cached, and is abandoned after 10 seconds. If a tool call returns 401 or 403, Claude Code re-runs the helper and retries the call once.</p>
<h2 id="authentication-with-oauth">Authentication with OAuth</h2>
<p>Many hosted servers use OAuth instead of static tokens. Add the server, then sign in:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp add --transport http sentry https://mcp.sentry.dev/mcp</code></pre></div>
<p>Inside Claude Code, run <code>/mcp</code> and complete the browser login. From the shell:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp login sentry
claude mcp login sentry --no-browser   # print the URL instead of opening a browser</code></pre></div>
<p><code>claude mcp login</code> prints the URL on its own when no local browser is available, which helps over SSH. <code>claude mcp logout &lt;name&gt;</code> clears stored credentials. If a server needs a pre-registered redirect URI, fix the port with <code>--callback-port</code>. <code>claude -p</code> and Agent SDK runs cannot run the OAuth flow, so sign in from an interactive session first. Removing a remote server also deletes the OAuth tokens and client registration stored for it.</p>
<h2 id="managing-and-debugging-servers">Managing and debugging servers</h2>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude mcp list              # all servers and their status
claude mcp get notion        # details for one server
claude mcp remove notion     # remove it</code></pre></div>
<p><code>claude mcp list</code> shows states such as <code>✔ Connected</code>, <code>! Needs authentication</code>, <code>✘ Failed to connect</code> and <code>⏸ Pending approval</code>. Inside a session, <code>/mcp</code> lets you sign in, turn a server off without losing its configuration, and reconnect it; <code>/mcp reconnect all</code> retries every failed server.</p>
<p>If a slow server will not start, raise the startup timeout (in milliseconds):</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">MCP_TIMEOUT=10000 claude</code></pre></div>
<p>A <code>&quot;timeout&quot;</code> field in a server&#39;s <code>.mcp.json</code> entry sets that server&#39;s tool timeout in milliseconds and overrides <code>MCP_TOOL_TIMEOUT</code> for it.</p>
<p>If you already set servers up in Claude Desktop, <code>claude mcp add-from-claude-desktop</code> imports them on macOS and WSL. In the other direction, <code>claude mcp serve</code> runs Claude Code itself as a stdio MCP server for other clients.</p>
<h2 id="context-output-limits-and-tool-search">Context, output limits and tool search</h2>
<p>Every tool definition costs context. Claude Code handles this with <strong>tool search</strong>, on by default on supported models: only tool names and server instructions load at session start, and full definitions load when Claude needs them. You can tune it with <code>ENABLE_TOOL_SEARCH</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">ENABLE_TOOL_SEARCH=auto:5 claude   # load upfront while definitions total under 5% of context
ENABLE_TOOL_SEARCH=false claude    # load everything upfront</code></pre></div>
<p>Claude Code turns tool search off when <code>ANTHROPIC_BASE_URL</code> points to a non-first-party host, because most proxies do not forward the blocks it needs; setting <code>ENABLE_TOOL_SEARCH</code> explicitly overrides that.</p>
<p>To keep a server&#39;s tools always visible, set <code>&quot;alwaysLoad&quot;: true</code> on it. Each upfront tool uses context, and startup waits for that server&#39;s tools (up to the 5-second connect timeout), so reserve this for the few tools Claude needs on every turn.</p>
<p>Tool output is capped as well. Claude Code warns when a tool returns more than 10,000 tokens, and the default maximum is 25,000 tokens. You can raise it:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">MAX_MCP_OUTPUT_TOKENS=50000 claude</code></pre></div>
<p>Separately, a successful text result longer than 50,000 characters is saved to a file and replaced with a file reference, whatever <code>MAX_MCP_OUTPUT_TOKENS</code> is set to. Database servers hit these limits most, because a full schema or large query result easily goes over. A server author can raise the threshold for one tool, such as <code>get_schema</code>, with the <code>anthropic/maxResultSizeChars</code> annotation, up to 500,000 characters.</p>
<h2 id="security-approval-prompt-injection-and-team-controls">Security: approval, prompt injection and team controls</h2>
<p>The documentation says to verify you trust each server before connecting it, and warns that servers fetching external content can expose you to prompt injection. A server can read data and act on your behalf, so review it as you would a new dependency.</p>
<p>Built-in safeguards:</p>
<ul>
<li><strong>Project servers need approval.</strong> In interactive sessions, Claude Code asks before using servers from a cloned <code>.mcp.json</code>. Reset those choices with <code>claude mcp reset-project-choices</code>.</li>
<li><strong>Headless runs do not ask.</strong> In <code>claude -p</code>, Agent SDK and cloud sessions, project servers load without a prompt. To block one, add it to <code>disabledMcpjsonServers</code> in settings. This matters if you run Claude Code in CI.</li>
<li><strong>Per-tool approval.</strong> A server author can mark a tool with <code>anthropic/requiresUserInteraction</code> so it prompts on every call, even in <code>acceptEdits</code>, <code>auto</code> and <code>bypassPermissions</code> modes. In <code>dontAsk</code> mode the call is denied.</li>
</ul>
<p>For organizations, administrators can control MCP centrally:</p>
<ul>
<li>A <code>managed-mcp.json</code> file at a system path (<code>/Library/Application Support/ClaudeCode/</code> on macOS, <code>/etc/claude-code/</code> on Linux and WSL, <code>C:\Program Files\ClaudeCode\</code> on Windows) takes exclusive control. Users cannot add or use other servers, including plugin servers. An empty <code>mcpServers</code> map blocks every server except the few that still load under exclusive control.</li>
<li><code>allowedMcpServers</code> and <code>deniedMcpServers</code> in managed settings filter what users configure. Match on <code>serverUrl</code> or <code>serverCommand</code>. The docs say a <code>serverName</code> entry &quot;is not a security control&quot;, because a user can call any server <code>github</code>.</li>
<li>With <code>managed-mcp.json</code> deployed, Claude in Chrome is blocked in terminal sessions unless <code>allowClaudeInChromeWithManagedMcp</code> is set in the device&#39;s own managed settings.</li>
</ul>
<h2 id="where-to-start">Where to start</h2>
<p>Add one remote server over HTTP, sign in with <code>/mcp</code>, and check <code>claude mcp list</code> before adding more. Put team-wide servers in <code>.mcp.json</code> with <code>${VAR}</code> placeholders, and keep personal credentials in local or user scope. Leave tool search on, and raise output limits only for servers that need it, which usually means databases. Before anything runs unattended, decide which project servers a headless run may load. A CLAUDE.md file is a good place to tell the agent when to use each server.</p>
<p>For more on running several Claude Code sessions as a team, see the <a href="/atlas/">Atlas home page</a>.</p>
]]></content:encoded></item>
<item><title>Claude Code Costs: Cut Token Usage and Stretch Limits</title><link>https://bridge-terminal.com/atlas/blog/claude-code-costs-token-usage-limits/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-costs-token-usage-limits/</guid><pubDate>Tue, 13 Oct 2026 13:35:00 GMT</pubDate><description>Where Claude Code spends tokens and how to cut it: /usage, model and effort choice, /clear vs /compact, prompt cache lifetime and team spend limits.</description><category>claude-code</category><category>ai-agents</category><category>cost-management</category><category>token-usage</category><content:encoded><![CDATA[<ul><li>To cut Claude Code costs, keep the conversation small and use the cheapest model and effort level that can do the job.</li><li>Every request re-sends the whole conversation, so long sessions that were never cleared are a common cause of unexpectedly high spend.</li><li>Start fresh with /clear when you switch to unrelated work, and use Sonnet for most coding tasks instead of leaving Opus as the default.</li><li>Lower the effort level for simple work: extended thinking is on by default and thinking tokens are billed as output tokens.</li><li>Run /usage to see token counts and, on subscription plans, which skills, subagents, MCP servers and loops use the most of your limits.</li></ul><p>To cut Claude Code costs, keep the context small and use the cheapest model and effort level that can do the job. Every request re-sends the whole conversation. Anthropic&#39;s docs say that unexpectedly high spend on an API or cloud-provider plan usually comes from long sessions that were never cleared or from Opus left as the default model. Below: how to see where your usage goes, which habits matter most, and which controls a team lead has on each type of plan.</p>
<p>The details come from Anthropic&#39;s Claude Code documentation on costs and prompt caching. Commands and plan details change between versions, so run <code>claude --version</code> and check the docs if something on your screen doesn&#39;t match.</p>
<h2 id="how-claude-code-costs-and-usage-limits-work">How Claude Code costs and usage limits work</h2>
<p>Claude Code is billed by API token consumption. What that means for you depends on how you sign in:</p>
<ul>
<li><strong>Pro and Max (individual subscriptions):</strong> usage is included in the plan, up to its limits. Past a limit, you can turn on usage credits to keep working.</li>
<li><strong>Team and Enterprise:</strong> each member&#39;s Claude Code usage comes out of a per-seat allowance that resets on a rolling five-hour window and a weekly window. The allowance is shared with Claude chat and Cowork, and its size depends on the seat tier (Standard or Premium).</li>
<li><strong>Claude Console (API key):</strong> billed per token to your organization, with workspace spend limits.</li>
<li><strong>Amazon Bedrock, Google Cloud&#39;s Agent Platform, Microsoft Foundry:</strong> billed per token to your cloud account. Spend controls are your cloud provider&#39;s budget tools.</li>
</ul>
<p>For scale, Anthropic reports an average across enterprise deployments of about $13 per developer per active day and $150–250 per developer per month, with 90% of users staying under $30 per active day. Your numbers will depend on model choice, codebase size, and how much you run in parallel or in automation. Anthropic recommends a small pilot group to establish a baseline before a wider rollout.</p>
<h2 id="measure-first-usage-context-and-insights">Measure first: /usage, /context and /insights</h2>
<p><strong><code>/usage</code></strong> opens with a Session block showing token counts by model (input, output, cache read, cache write) and a dollar figure. Claude Code calculates that figure locally at list price, so treat it as an estimate; authoritative billing is on the Usage page in the Claude Console. Since v2.1.211 the Session totals reset when you run <code>/clear</code>. From v2.1.251 there is also a <code>Prompt cache (main)</code> line with the share of input served from cache, the number of misses, and whether the cache is warm right now. It covers the main conversation only, not subagents.</p>
<p>On a subscription, the session dollar figure doesn&#39;t affect your bill. On Pro, Max, Team and Enterprise plans, what matters is the plan usage bars and the breakdown below them:</p>
<ul>
<li><strong>Attribution:</strong> the share of recent usage from skills, subagents, plugins and individual MCP servers.</li>
<li><strong>Behavior flags:</strong> patterns such as long context or cache misses, flagged when one accounts for 10% or more of recent usage, each with a tip for reducing it.</li>
<li><strong>Loops:</strong> the heaviest <code>/loop</code> and other scheduled tasks, with total and per-run tokens (v2.1.242 or later).</li>
</ul>
<p>Press <code>d</code> or <code>w</code> to switch between the last 24 hours and the last 7 days. The breakdown is approximate and built from session history on your machine, so it leaves out usage from other devices and from claude.ai.</p>
<p><strong><code>/context</code></strong> shows what is taking up the context window right now. Use it when sessions feel expensive and you don&#39;t know why.</p>
<p><strong><code>/insights</code></strong> writes an HTML report on <em>how</em> you work: what you work on, friction points such as misunderstood requests or buggy code, and suggestions. The latest report is saved to <code>~/.claude/usage-data/report.html</code>. The analysis itself counts against your plan or API usage.</p>
<h2 id="the-habits-that-save-the-most">The habits that save the most</h2>
<p>The docs name two habits as the highest-impact: clearing between unrelated tasks and matching the model to the job.</p>
<h3 id="use-clear-between-unrelated-tasks">Use /clear between unrelated tasks</h3>
<p>Claude Code sends the full conversation with every request, plus another request each time Claude uses tools. Prompt caching bills re-reads at the lower cached-token rate, but a one-line question in a session that has been open all day still draws usage for the whole conversation.</p>
<p>When you move to unrelated work, start fresh:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">/rename auth-refactor
/clear</code></pre></div>
<p><code>/rename</code> lets you find the old session later with <code>/resume</code>. <code>/clear</code> costs nothing. <code>/compact</code> sends a request containing the whole conversation to produce a summary. While the cache is warm, most of that is read from cache, but after a break longer than the cache lifetime it reprocesses the full history as uncached input. Compact at a natural break when you need continuity; clear when you don&#39;t. If you went down a path you want to abandon, <code>/rewind</code> to an earlier turn instead: it goes back to a prefix that is already cached.</p>
<p>If you do compact, tell it what to keep:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">/compact Focus on code samples and API usage</code></pre></div>
<p>You can also put standing compaction instructions in CLAUDE.md:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown"># Compact instructions

When you are using compact, please focus on test output and code changes</code></pre></div>
<h3 id="match-the-model-to-the-task">Match the model to the task</h3>
<p>The docs say Sonnet handles most coding tasks well and costs less than Opus, and recommend reserving Opus for complex architectural decisions or multi-step reasoning. Switch mid-session with <code>/model</code>, or set a default in <code>/config</code>.</p>
<p>Three things are easy to miss:</p>
<ul>
<li>Switching to Opus also switches the subagents that inherit your session&#39;s model.</li>
<li>For simple subagent tasks, set <code>model: haiku</code> in the subagent&#39;s configuration. For more on when subagents are worth it, see <a href="/atlas/blog/claude-code-subagents-when-to-split-work/">Claude Code Subagents: When to Split Work and How</a>.</li>
<li>Each model has its own prompt cache, so a mid-session model switch makes the next request reprocess the whole conversation. Pick the model at the start of a session where you can.</li>
</ul>
<h3 id="lower-effort-for-simple-work">Lower effort for simple work</h3>
<p>Extended thinking is on by default, and thinking tokens are billed as output tokens. Depending on the model, the default budget can be tens of thousands of tokens per request. For routine work, lower the effort level with <code>/effort</code> or in <code>/model</code>, or disable thinking in <code>/config</code>. Opus 5.5, Sonnet 5.5, Haiku 5.5 and the Fable models always use extended thinking and can&#39;t have it turned off, so effort is the setting to adjust there.</p>
<p>Changing effort mid-session also has a cache cost on most models: the next request reprocesses the conversation. The exception is Opus 5.5, Sonnet 5.5, Haiku 5.5 and Fable 5.1 used with an API key or a Claude subscription, where the cache is kept (not on Bedrock, Google Cloud&#39;s Agent Platform or a Claude apps gateway).</p>
<p>On models with a fixed thinking budget, you can also lower it with an environment variable:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">MAX_THINKING_TOKENS=8000</code></pre></div>
<p>Adaptive-reasoning models ignore nonzero budgets, so use effort levels with those instead.</p>
<h3 id="write-specific-prompts-and-plan-before-building">Write specific prompts and plan before building</h3>
<p>A vague prompt like &quot;improve this codebase&quot; triggers broad scanning. &quot;Add input validation to the login function in auth.ts&quot; lets Claude read only what it needs. For bigger changes:</p>
<ul>
<li>Press Shift+Tab to cycle to <strong>plan mode</strong>, so Claude proposes an approach for your approval before writing code. Re-work after a wrong first direction is expensive.</li>
<li>Press <strong>Escape</strong> as soon as Claude heads the wrong way. Use <code>/rewind</code> or double-tap Escape to restore an earlier checkpoint.</li>
<li>Give it <strong>verification targets</strong> such as test cases or expected output, so Claude catches its own mistakes before you have to ask for fixes.</li>
</ul>
<h2 id="shrink-the-context-claude-starts-with">Shrink the context Claude starts with</h2>
<p>Anything loaded at session start is carried in every request of that session.</p>
<ul>
<li><strong>Keep CLAUDE.md short.</strong> The docs suggest staying under 200 lines and moving workflow-specific instructions, such as PR reviews or database migrations, into skills, which load only when invoked. Our <a href="/atlas/blog/claude-md-best-practices-project-instructions/">CLAUDE.md best practices</a> covers what belongs in the file.</li>
<li><strong>Trim MCP servers.</strong> MCP tool definitions are deferred by default, so only tool names and server instructions enter context until a tool is used. Still, run <code>/mcp</code> and disable servers you aren&#39;t using. Where a CLI exists (<code>gh</code>, <code>aws</code>, <code>gcloud</code>, <code>sentry-cli</code>), it is more context-efficient than an MCP server because it adds no per-tool listing.</li>
<li><strong>Install code intelligence plugins</strong> for typed languages. One &quot;go to definition&quot; call can replace a grep followed by reading several candidate files.</li>
</ul>
<h2 id="filter-noisy-output-before-claude-reads-it">Filter noisy output before Claude reads it</h2>
<p>Test runners and logs fill context fast. A PreToolUse hook can rewrite a command so Claude sees only what matters. This is the example from the docs, which pipes test output through a filter for failures. Add it to <code>settings.json</code>:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;~/.claude/hooks/filter-test-output.sh&quot;
          }
        ]
      }
    ]
  }
}</code></pre></div>
<p>Create the folder with <code>mkdir -p ~/.claude/hooks</code>, save the script as <code>~/.claude/hooks/filter-test-output.sh</code>, and make it executable with <code>chmod +x</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">#!/bin/bash
input=$(cat)
cmd=$(echo &quot;$input&quot; | jq -r '.tool_input.command')

# If running tests, filter to show only failures
if [[ &quot;$cmd&quot; =~ ^(npm test|pytest|go test) ]]; then
  filtered_cmd=&quot;$cmd 2&gt;&amp;1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100&quot;
  echo &quot;$input&quot; | jq --arg filtered &quot;$filtered_cmd&quot; \
    '{hookSpecificOutput: {hookEventName: &quot;PreToolUse&quot;, permissionDecision: &quot;allow&quot;, updatedInput: (.tool_input + {command: $filtered})}}'
else
  echo &quot;{}&quot;
fi</code></pre></div>
<p>Run <code>/hooks</code> and check that it appears under PreToolUse. Another option is to delegate verbose jobs such as test runs, documentation fetches and log processing to a subagent, so only a summary returns to your main conversation. The subagent&#39;s own requests still count against your usage, so give it a smaller model.</p>
<h2 id="why-usage-climbs-in-a-long-session">Why usage climbs in a long session</h2>
<p>If your limits drain faster than your activity seems to justify, check these:</p>
<ul>
<li><strong>Cache misses after breaks.</strong> On a subscription, within plan usage, the main conversation&#39;s prompt cache lasts an hour. It drops to five minutes once you&#39;re drawing on usage credits, and five minutes is the default on API keys and cloud providers. The <code>promptCacheTtl</code> setting (v2.1.242 or later) lets you choose <code>1h</code> yourself; one-hour cache writes are billed at a higher rate. Your first message after a longer break reprocesses the full context. On Pro and Max, Claude Code offers to resume a large session from a summary after a long break.</li>
<li><strong>Scheduled tasks and loops</strong> fire on their interval while the session is idle, and each run sends your full context.</li>
<li><strong>Subagents</strong> send their own requests on top of the main conversation&#39;s, and get a five-minute cache by default even on a subscription. The attribution breakdown in <code>/usage</code> shows their share.</li>
<li><strong>Agent teams.</strong> Each teammate has its own context window, and the docs estimate about 7x the tokens of a standard session when teammates run in plan mode. Teams are off by default. If you enable them, keep teams small, use Sonnet for teammates, and shut teammates down when they finish.</li>
<li><strong>Background requests</strong>, such as summarizing past conversations for <code>claude --resume</code>, are small: typically under $0.04 per session.</li>
</ul>
<h2 id="hitting-a-limit-what-each-message-means">Hitting a limit: what each message means</h2>
<ul>
<li><strong>&quot;You&#39;ve hit your session limit&quot; / &quot;weekly limit&quot;:</strong> a subscription usage window shared across all models, so switching models won&#39;t help. Wait for the reset time shown, or run <code>/usage-credits</code> if usage credits are turned on.</li>
<li><strong>&quot;You&#39;ve hit your Opus limit&quot; / &quot;Sonnet limit&quot;:</strong> model-specific. Switching to a model outside that family with <code>/model</code> lets you keep working.</li>
<li><strong>Spend limit messages</strong> (individual, org monthly, or team shared budget): usage credits have reached a cap an admin set. An admin raises it under Organization settings &gt; Usage.</li>
<li><strong>Auto-compact warnings</strong> aren&#39;t usage limits. They mean the conversation is close to its auto-compact window.</li>
</ul>
<h2 id="controls-for-team-leads">Controls for team leads</h2>
<ul>
<li><strong>Team and Enterprise:</strong> the org analytics spend report shows estimated spend per user and per model, with CSV export. It covers usage-credit spend and appears once usage credits are on; usage inside the seat allowance isn&#39;t metered in dollars. With usage credits on, spend limits can be set per organization, group or member.</li>
<li><strong>Console:</strong> set workspace spend limits. A &quot;Claude Code&quot; workspace is created automatically the first time someone authenticates with a Console account. For organizations with custom rate limits, a workspace rate limit on it caps Claude Code&#39;s share so it can&#39;t crowd out other production workloads.</li>
<li><strong>Cloud providers:</strong> Anthropic&#39;s analytics don&#39;t cover this usage. For per-user attribution, use OpenTelemetry export, a self-hosted Claude apps gateway, or an LLM gateway.</li>
<li><strong>Contracted rates:</strong> the <code>modelPricing</code> managed setting (v2.1.242 or later) makes the figures in <code>/usage</code>, the status line and OpenTelemetry reflect your contracted prices instead of list price. It changes what Claude Code reports, not what you are charged.</li>
</ul>
<p>If you run several agents in parallel, budget for each one, because every session has its own context. On <a href="/atlas/">Atlas</a>, where each engineer is its own Claude Code session with model and effort tuned to its role, an optional daily budget warns at 50% and 80% and pauses the team at 100%.</p>
<h2 id="summary">Summary</h2>
<p>Most Claude Code spend comes from context you no longer need and from models or effort levels set higher than the task requires. Clear between tasks, default to Sonnet, lower effort for routine work, keep CLAUDE.md and MCP lean, and filter noisy output before Claude reads it. Then check the <code>/usage</code> breakdown over the last 7 days to see which changes made a difference.</p>
]]></content:encoded></item>
<item><title>AI Code Review with Claude Code: What It Catches and Misses</title><link>https://bridge-terminal.com/atlas/blog/ai-code-review-claude-code-setup/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/ai-code-review-claude-code-setup/</guid><pubDate>Mon, 12 Oct 2026 03:45:00 GMT</pubDate><description>Set up AI code review in Claude Code with /code-review, ultrareview, managed Code Review or GitHub Actions, and learn what it catches, misses and costs.</description><category>claude-code</category><category>code-review</category><category>ai-agents</category><category>github-actions</category><content:encoded><![CDATA[<ul><li>Claude Code's review looks for correctness bugs in a change, such as logic errors, broken edge cases, security vulnerabilities and regressions, and reads the change against the rest of the codebase.</li><li>You can run it four ways: local /code-review, a deeper cloud ultrareview, managed Code Review on GitHub pull requests (Team and Enterprise), or your own claude-code-action workflow.</li><li>Managed Code Review never approves or blocks a pull request: its check run always ends neutral. If you want a merge gate, you build it from the check run's severity counts.</li><li>A short REVIEW.md at the repository root tunes what managed Code Review flags. Local /code-review follows CLAUDE.md but doesn't read REVIEW.md.</li><li>Managed reviews average $15–25 each, and after three free runs on Pro and Max, ultrareview typically costs $5–25. The trigger you choose is the main cost lever.</li></ul><p>Claude Code&#39;s review looks for correctness bugs in a diff: logic errors, broken edge cases, security vulnerabilities and regressions, read against the rest of your codebase. By default, the managed reviewer doesn&#39;t flag formatting preferences or missing test coverage. It never approves or blocks a pull request, and it doesn&#39;t know whether the change was the right thing to build. Use it as a second reviewer that runs before a person looks, and tune it with a <code>REVIEW.md</code> file so it flags what your team cares about.</p>
<p>This guide covers the four ways to run it, what each one is for, and the settings that decide whether you get useful findings or noise.</p>
<h2 id="four-ways-to-run-ai-code-review-in-claude-code">Four ways to run AI code review in Claude Code</h2>
<div class="table"><table><thead><tr><th>Option</th><th>Where it runs</th><th>Typical use</th><th>Cost model</th></tr></thead><tbody><tr><td><code>/code-review</code></td><td>Your local session</td><td>Quick check while you work</td><td>Counts toward normal usage</td></tr><tr><td><code>/code-review ultra</code> (ultrareview)</td><td>Anthropic cloud sandbox</td><td>Deeper pass before merging a substantial change</td><td>3 free runs on Pro and Max, then usage credits</td></tr><tr><td>Code Review (managed)</td><td>Anthropic infrastructure, via the Claude GitHub App</td><td>Automatic review on pull requests</td><td>Usage credits, billed by tokens</td></tr><tr><td><code>claude-code-action</code> workflow</td><td>Your GitHub Actions runners</td><td>Review where you control the prompt, model and triggers</td><td>API key or subscription token, plus Actions minutes</td></tr></tbody></table></div>
<h3 id="local-review-with-code-review">Local review with /code-review</h3>
<div class="code"><span class="lang">text</span><pre><code class="language-text">/code-review</code></pre></div>
<p>It reviews your branch&#39;s commits ahead of its upstream plus any uncommitted changes. You can also pass a target: a file path, a PR number, a branch name, or a ref range such as <code>main...my-feature</code>. It runs as a background subagent with its own context window, so it doesn&#39;t fill your conversation, and the findings arrive there when it finishes. <code>/review</code> is an alias for it.</p>
<p>Flags:</p>
<ul>
<li><code>--fix</code> applies the findings to your working tree after the review.</li>
<li><code>--comment</code> posts findings as inline comments on a GitHub pull request, or as a single note on a GitLab merge request.</li>
<li><code>--max-findings &lt;n&gt;</code> (or <code>all</code>, or <code>default</code>) replaces the usual limit on how many findings it reports. It needs Claude Code v2.1.288 or later.</li>
</ul>
<p>You can also pass an effort level. At <code>low</code> it reports only the findings it&#39;s most confident in, so you see fewer false positives; from <code>medium</code> through <code>max</code> it covers more ground. If you don&#39;t give a level, it reuses the last one you typed. One catch: <code>/rewind</code> doesn&#39;t undo the edits a background review makes with <code>--fix</code>, so revert them with git.</p>
<h3 id="deeper-review-with-ultrareview">Deeper review with ultrareview</h3>
<div class="code"><span class="lang">text</span><pre><code class="language-text">/code-review ultra
/code-review ultra 1234
/code-review ultra develop</code></pre></div>
<p>These review your current branch against the default branch, then pull request #1234 on github.com, then your branch against <code>develop</code>. Ultrareview is a research preview. It runs a larger group of reviewer agents in a cloud sandbox, and every finding it reports is independently reproduced and verified. A review typically takes 15 to 25 minutes.</p>
<p>Pro and Max accounts get three free runs, a one-time allotment that doesn&#39;t refresh. After that a review typically costs $5 to $25 in usage credits, depending on the size of the change, and the launch dialog shows an estimate first. A branch review can include up to 500 changed files and 8,000 changed lines by default. Those limits can change, and the refusal message names the ones in effect.</p>
<p>For scripts and CI, use the subcommand. It waits for the review to finish (up to 45 minutes by default) and prints the findings to stdout; <code>--json</code> prints the raw findings payload:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude ultrareview 1234 --json</code></pre></div>
<p>Don&#39;t use <code>claude -p &#39;/code-review ultra&#39;</code> for this: it launches the cloud review and exits without the findings. Ultrareview needs a claude.ai sign-in and isn&#39;t available on Amazon Bedrock, Google Cloud&#39;s Agent Platform or Microsoft Foundry, or to organizations with Zero Data Retention or the HIPAA configuration. Where it isn&#39;t available, <code>/code-review ultra</code> runs a local review instead.</p>
<h3 id="managed-code-review-on-every-pull-request">Managed Code Review on every pull request</h3>
<p>Code Review is a research preview for Team and Enterprise plans, with the same Zero Data Retention and HIPAA exclusions. An Owner opens <code>claude.ai/admin-settings/claude-code</code>, installs the Claude GitHub App, selects repositories and picks a trigger for each:</p>
<ul>
<li><strong>Once after PR creation</strong>: one review when the PR is opened or marked ready for review.</li>
<li><strong>After every push</strong>: a review on each push. It catches new issues as the PR changes and auto-resolves threads when you fix flagged issues, but it runs the most reviews and costs the most.</li>
<li><strong>Manual</strong>: reviews run only when someone comments <code>@claude review</code> (a single review) or <code>@claude review always</code> (a review, plus reviews on later pushes).</li>
</ul>
<p>Several agents analyze the diff and surrounding code in parallel, each looking for a different class of issue. A verification step then checks candidates against how the code actually behaves, to filter out false positives. Findings arrive as inline comments tagged Important (fix before merging), Nit (minor, not blocking) or Pre-existing (a real bug this PR didn&#39;t introduce). Anthropic&#39;s docs put the average at $15–25 per review, scaling with PR size and complexity, and about 20 minutes per review. Code Review is billed through usage credits, not your plan&#39;s included usage.</p>
<h3 id="your-own-workflow-with-claude-code-action">Your own workflow with claude-code-action</h3>
<p>To run review on your own runners and control the prompt, use the GitHub Action. The quickest route is <code>/install-github-app</code> inside Claude Code, selecting the review workflow. The manual version runs the <code>code-review</code> plugin when a PR is opened, updated, reopened or marked ready for review:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">name: Code Review
on:
  pull_request:
    types: [opened, synchronize, ready_for_review, reopened]
jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
      issues: read
      id-token: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          plugin_marketplaces: &quot;https://github.com/anthropics/claude-code.git&quot;
          plugins: &quot;code-review@claude-code-plugins&quot;
          prompt: &quot;/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}&quot;
          claude_args: '--allowedTools &quot;mcp__github_inline_comment__create_inline_comment&quot;'</code></pre></div>
<p>Keep both <code>--comment</code> and the <code>claude_args</code> line. Without <code>--comment</code>, Claude posts nothing and the findings appear only in the workflow log. The action starts the inline-comment tool only when <code>--allowedTools</code> in <code>claude_args</code> names it. To use a Claude subscription instead of an API key, use <code>claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}</code>. For more on running Claude non-interactively, see our guide to <a href="/atlas/blog/claude-code-headless-ci-permissions/">Claude Code headless mode in CI</a>.</p>
<h2 id="what-ai-code-review-catches">What AI code review catches</h2>
<p>By default, managed Code Review focuses on correctness: &quot;bugs that would break production, not formatting preferences or missing test coverage.&quot; That covers:</p>
<ul>
<li><strong>Logic errors and broken edge cases.</strong> The docs&#39; examples: a token refresh that races with logout and leaves stale sessions active, and a parser that silently returns 0 on malformed input.</li>
<li><strong>Security vulnerabilities.</strong></li>
<li><strong>Subtle regressions</strong>, found by reading the change in the context of the full codebase, not only the diff.</li>
<li><strong>Pre-existing bugs</strong> that the PR didn&#39;t introduce, tagged separately.</li>
<li><strong>Rule violations and stale docs.</strong> Code Review reads your <code>CLAUDE.md</code> files, flags newly introduced violations as nits, and flags changes that make a <code>CLAUDE.md</code> statement outdated.</li>
</ul>
<h2 id="what-it-misses-or-leaves-to-you">What it misses, or leaves to you</h2>
<ul>
<li><strong>It doesn&#39;t gate merges.</strong> The check run always ends with a neutral conclusion, so branch protection never blocks on it.</li>
<li><strong>It doesn&#39;t hold a conversation.</strong> Replying to an inline finding gets no response. Fix the code and push, or resolve the thread to dismiss it.</li>
<li><strong>Some PRs are skipped.</strong> Managed Code Review never reviews fork PRs automatically; someone with write access to the base repository must comment <code>@claude review</code>. The Action workflow skips draft and closed PRs, PRs that already have a comment from Claude, and PRs it judges don&#39;t need review, such as automated or trivial ones. On public repositories, GitHub withholds secrets from fork-triggered runs, so the Action reviews only PRs from branches in the same repository.</li>
<li><strong>Runs can fail.</strong> Reviews are best-effort. Code Review retries some interrupted reviews itself; otherwise the check run title says the review failed or timed out, and you rerun it with <code>@claude review</code> or, for non-fork PRs, the Re-run button.</li>
<li><strong>Big diffs hit limits.</strong> Ultrareview refuses diffs over its size limits, so split large changes.</li>
<li><strong>It can&#39;t judge intent.</strong> It checks the code for bugs, not whether the feature matches what was asked for or whether the change should exist at all.</li>
</ul>
<h2 id="tune-what-it-flags-with-review-md">Tune what it flags with REVIEW.md</h2>
<p>For managed Code Review, the main lever is a <code>REVIEW.md</code> at the repository root. Its contents go directly to the agents that find and verify findings, so rules there land more reliably than the same rules in a long <code>CLAUDE.md</code>. Local <code>/code-review</code> follows <code>CLAUDE.md</code> but doesn&#39;t read <code>REVIEW.md</code>.</p>
<p>The docs list these as the patterns with the most impact:</p>
<ul>
<li><strong>Redefine Important</strong> for your repo. A docs, config or prototype repo might want a much narrower definition.</li>
<li><strong>Cap nits</strong>, for example &quot;report at most five nits, mention the rest as a count in the summary.&quot;</li>
<li><strong>Skip what you don&#39;t need reviewed</strong>: generated code, lockfiles, vendored dependencies, and anything CI already enforces.</li>
<li><strong>Add repo-specific checks</strong>, such as &quot;new API routes must have an integration test.&quot;</li>
<li><strong>Set a verification bar</strong>, for example &quot;behavior claims need a <code>file:line</code> citation in the source, not an inference from naming.&quot;</li>
<li><strong>Converge on re-review</strong>: &quot;after the first review, suppress new nits and post Important findings only.&quot;</li>
</ul>
<p>A short example, adapted from the docs:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown"># Review instructions

## What Important means here
Incorrect logic, unscoped database queries, PII in logs, and
migrations that aren't backward compatible. Style is Nit at most.

## Do not report
- Lint, formatting and type errors (CI enforces these)
- Files under `src/gen/` and any `*.lock` file

## Always check
- Database queries are scoped to the caller's tenant</code></pre></div>
<p>Keep it short: the docs warn that a long <code>REVIEW.md</code> dilutes the rules that matter most. General project context belongs in <code>CLAUDE.md</code>; see our <a href="/atlas/blog/claude-md-best-practices-project-instructions/">CLAUDE.md best practices</a>.</p>
<h2 id="gate-merges-on-findings-yourself">Gate merges on findings yourself</h2>
<p>To fail CI on Important findings, read the check run&#39;s output. Its Details text ends with a machine-readable comment. Find the check run ID first:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">gh api repos/OWNER/REPO/commits/COMMIT_SHA/check-runs --jq '.check_runs[] | {id, name}'</code></pre></div>
<p>Take the <code>id</code> of the <code>Claude Code Review</code> run, then parse the severity counts:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">gh api repos/OWNER/REPO/check-runs/CHECK_RUN_ID \
  --jq '.output.text | split(&quot;bughunter-severity: &quot;)[1] | split(&quot; --&gt;&quot;)[0] | fromjson'</code></pre></div>
<p>This returns counts such as <code>{&quot;normal&quot;: 2, &quot;nit&quot;: 1, &quot;pre_existing&quot;: 0}</code>, where <code>normal</code> is the number of Important findings. Fail the job when it&#39;s above zero, and keep a way for a person to override it.</p>
<h2 id="keep-costs-predictable">Keep costs predictable</h2>
<ul>
<li><strong>Choose the trigger deliberately.</strong> &quot;After every push&quot; multiplies cost by the number of pushes; &quot;Once after PR creation&quot; runs one review per PR; &quot;Manual&quot; costs only what someone asks for. In those last two modes, <code>@claude review always</code> adds per-push cost for that PR.</li>
<li><strong>Cap managed Code Review</strong> with a monthly spend cap at <code>claude.ai/admin-settings/usage</code>. The repositories table in admin settings shows average cost per review, and the analytics dashboard breaks spend down by repository.</li>
<li><strong>Cap the Action</strong>: set <code>--max-turns</code> in <code>claude_args</code>, add a workflow timeout, and use GitHub&#39;s concurrency controls.</li>
<li><strong>Match depth to the change</strong>: local <code>/code-review</code> while you iterate, ultrareview for substantial changes before merging.</li>
</ul>
<h2 id="a-review-setup-that-works">A review setup that works</h2>
<ol>
<li>Run <code>/code-review</code> locally before pushing.</li>
<li>Run one automatic review per PR, through managed Code Review or the Action, with a focused <code>REVIEW.md</code>.</li>
<li>Use ultrareview, or <code>@claude review always</code>, for risky changes such as auth, billing or migrations.</li>
<li>Have a person approve every merge, focusing on intent, design and anything tagged pre-existing.</li>
</ol>
<p>The same approach applies when agents write the code as well as review it: review each piece as it finishes, then review everything going out together before production. That&#39;s how <a href="/atlas/">Atlas</a> runs its AI engineering team. Every build is reviewed as it finishes and comes back either RELEASABLE or with fix tasks filed to its builder by name, and before a production release everything going out is reviewed together.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Claude Code&#39;s reviewers look for correctness problems in the context of the wider codebase. They won&#39;t block merges, judge product intent or replace a human approval. Start with one review per PR and a short <code>REVIEW.md</code> that defines Important for your repo, add a merge gate only if you need one, and save ultrareview for changes where a missed bug would cost the most.</p>
]]></content:encoded></item>
<item><title>Claude Code Headless in CI: -p Mode and Safe Permissions</title><link>https://bridge-terminal.com/atlas/blog/claude-code-headless-ci-permissions/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-headless-ci-permissions/</guid><pubDate>Sun, 11 Oct 2026 03:35:00 GMT</pubDate><description>Run Claude Code headless in CI with claude -p: bare mode, dontAsk and allowedTools permissions, JSON output, exit codes and cost caps for unattended jobs.</description><category>claude-code</category><category>ci-cd</category><category>ai-agents</category><category>permissions</category><content:encoded><![CDATA[<ul><li>To run Claude Code with nobody watching, use claude -p with a prompt and set the permissions yourself before the run starts.</li><li>Add --bare so the run skips hooks, MCP servers, plugins and CLAUDE.md found on the runner. Without it, a -p run executes a checked-out repo's hooks with no trust prompt.</li><li>Set the permission mode explicitly, since the built-in starting mode can be auto. Start with dontAsk and allow only the tools the job needs, using prefix rules such as Bash(git diff *).</li><li>Request --output-format json and check the exit code. A failure inside the run, such as missing authentication, is printed on stdout as the result.</li><li>Cap spend with --max-budget-usd (an estimate that can be exceeded) and set a CI job timeout. Pin the Claude Code version, because several behaviours need v2.1.259 or v2.1.286.</li></ul><p>To run Claude Code headless in CI, call <code>claude -p &quot;&lt;prompt&gt;&quot;</code> and set the permissions yourself. Add <code>--bare</code> so the run ignores any hooks, MCP servers and <code>CLAUDE.md</code> it finds on the runner. Choose a permission mode such as <code>dontAsk</code>, and use <code>--allowedTools</code> to list only the tools the job needs. Request <code>--output-format json</code> so your pipeline can parse the result, and branch on the exit code.</p>
<p>This guide covers each of those settings and the problems that appear when nobody is around to answer a permission prompt. The flags come from Anthropic&#39;s documentation on running Claude Code programmatically and from its CLI reference. For Codex, including approval modes and sandboxing, see our <a href="/atlas/blog/codex-cli-install-approval-modes-sandbox/">Codex CLI setup guide</a>.</p>
<h2 id="how-claude-code-non-interactive-mode-works">How Claude Code non-interactive mode works</h2>
<p>Add <code>-p</code> (or <code>--print</code>) to a <code>claude</code> command to run it non-interactively. Claude Code takes the prompt, works on it, prints the result and exits:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;What does the auth module do?&quot;</code></pre></div>
<p>Three behaviours matter in CI:</p>
<ul>
<li><strong>Exit codes.</strong> Claude Code exits with 0 on success and with a non-zero code when the run fails, so your script can branch on the status. An invalid flag produces an error on stderr before the run starts. A failure during the run, such as missing authentication, is printed as the result on <strong>stdout</strong>. Text on stdout doesn&#39;t mean the run succeeded, so check the exit code.</li>
<li><strong>Stdin works.</strong> You can pipe data in and redirect the answer out. Piped stdin is capped at 10MB. Past that limit, Claude Code exits with an error and a non-zero status. For larger inputs, write the content to a file and give its path in the prompt.</li>
<li><strong>Some commands are unavailable.</strong> Skills and custom commands still work if you put <code>/skill-name</code> in the prompt. Built-in commands that only run in the terminal interface, such as <code>/login</code>, don&#39;t.</li>
</ul>
<p>Piping also cuts down the permissions a job needs. The docs show a <code>package.json</code> script that pipes a diff into Claude, so Claude doesn&#39;t need Bash permission to read it:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;scripts&quot;: {
    &quot;lint:claude&quot;: &quot;git diff main | claude -p \&quot;you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\&quot;&quot;
  }
}</code></pre></div>
<p>If your pipeline can collect the data itself, pass the data to Claude rather than giving Claude a tool to fetch it.</p>
<h2 id="use-bare-mode-so-runs-are-reproducible">Use bare mode so runs are reproducible</h2>
<p>Without extra flags, <code>claude -p</code> finds and loads the same things an interactive session would: hooks, skills, custom commands, subagents, installed plugins, MCP servers, auto memory and <code>CLAUDE.md</code>, from both the working directory and <code>~/.claude</code>.</p>
<p>This matters most in CI. According to the documentation, a <code>-p</code> session without <code>--bare</code> runs the hooks in a project&#39;s <code>.claude/settings.json</code> and connects the servers in its <code>.mcp.json</code>, even in a folder you have never trusted. It shows no workspace trust dialog and asks for no approval per server. So if your pipeline checks out code you don&#39;t control, such as a pull request from a fork, that repository&#39;s configuration runs on your runner.</p>
<p><code>--bare</code> skips all of that loading:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude --bare -p &quot;Summarize README.md&quot; --allowedTools &quot;Read&quot;</code></pre></div>
<p>There is one exception. For a directory you add with <code>--add-dir</code>, bare mode still loads skills from its <code>.claude/skills/</code> folder, but it skips that directory&#39;s <code>.claude/commands/</code> and <code>.claude/agents/</code>.</p>
<p>In bare mode, Claude has the Bash, file read and file edit tools. Any other context has to be passed with a flag:</p>
<div class="table"><table><thead><tr><th>To load</th><th>Use</th></tr></thead><tbody><tr><td>System prompt additions</td><td><code>--append-system-prompt</code>, <code>--append-system-prompt-file</code></td></tr><tr><td>Settings</td><td><code>--settings &lt;file-or-json&gt;</code></td></tr><tr><td>MCP servers</td><td><code>--mcp-config &lt;file-or-json&gt;</code></td></tr><tr><td>Custom agents</td><td><code>--agents &lt;file-or-json&gt;</code></td></tr><tr><td>A plugin</td><td><code>--plugin-dir &lt;path&gt;</code>, <code>--plugin-url &lt;url&gt;</code></td></tr></tbody></table></div>
<p>Bare mode also changes how the session behaves:</p>
<ul>
<li>Only MCP servers given on the command line connect.</li>
<li>Background tasks don&#39;t run, so a command that reaches its timeout stops instead of moving to the background.</li>
<li>Claude doesn&#39;t get the system reminders that normally come with tool results. For example, it isn&#39;t told when a file it read earlier has changed on disk.</li>
</ul>
<p>Before v2.1.286, these limits were only partly in place.</p>
<p>Anthropic recommends <code>--bare</code> for scripted and SDK calls and says it will become the default for <code>-p</code> in a future release. Setting it now means your jobs won&#39;t change behaviour when that happens.</p>
<h3 id="authentication-in-bare-mode">Authentication in bare mode</h3>
<p>Bare mode never reads OAuth credentials or the system keychain, so a subscription login on the runner won&#39;t work. For the Anthropic API, set <code>ANTHROPIC_API_KEY</code> in the environment to a key created in the Claude Console, or supply an <code>apiKeyHelper</code> in the <code>--settings</code> JSON. Amazon Bedrock, Google Cloud&#39;s Agent Platform and Microsoft Foundry still read their own provider credentials. Keep the key in your CI system&#39;s secret store, not in the workflow file.</p>
<h2 id="safe-permissions-for-unattended-runs">Safe permissions for unattended runs</h2>
<p>In a headless run, nobody can answer a prompt. Decide in advance what is allowed, and make sure everything else is denied. Otherwise a run can hang, or be approved by a mode you didn&#39;t choose.</p>
<h3 id="always-set-a-permission-mode">Always set a permission mode</h3>
<p>If nothing sets a permission mode, the run starts in Claude Code&#39;s built-in starting mode, which can be <code>auto</code>. Pass the mode you want explicitly. The headless docs describe three options:</p>
<ul>
<li><strong><code>dontAsk</code></strong> denies every call that would otherwise prompt, and the docs suggest it for locked-down CI runs. Actions that need no approval in Manual mode still run, such as file reads in your working directories and the read-only command set. So do actions covered by your <code>--allowedTools</code> entries or <code>permissions.allow</code> rules. Some things are denied even when an allow rule matches, including <code>AskUserQuestion</code> and reads from network paths.</li>
<li><strong><code>acceptEdits</code></strong> lets Claude write files without prompting and auto-approves common filesystem commands such as <code>mkdir</code>, <code>touch</code>, <code>mv</code> and <code>cp</code>. Other shell commands (apart from read-only ones) and network requests still need an allow rule.</li>
<li><strong><code>auto</code></strong> has a classifier review most actions in place of a person.</li>
</ul>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Apply the lint fixes&quot; --permission-mode acceptEdits</code></pre></div>
<p>For most CI jobs, start with <code>dontAsk</code> and add allow rules one at a time. Use <code>acceptEdits</code> for jobs whose purpose is to change files, such as applying formatter or lint fixes on a branch.</p>
<h3 id="scope-tools-with-allowedtools">Scope tools with allowedTools</h3>
<p><code>--allowedTools</code> approves specific tools ahead of time:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Run the test suite and fix any failures&quot; \
  --allowedTools &quot;Bash,Read,Edit&quot;</code></pre></div>
<p>A bare <code>Bash</code> entry approves shell commands without asking, which is usually more than a CI job needs. The flag uses Claude Code&#39;s permission rule syntax, so you can approve command prefixes instead:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Look at my staged changes and create an appropriate commit&quot; \
  --allowedTools &quot;Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)&quot;</code></pre></div>
<p>The space before <code>*</code> matters. <code>Bash(git diff *)</code> matches commands that start with <code>git diff</code>. Without the space, <code>Bash(git diff*)</code> would also match <code>git diff-index</code>.</p>
<p>If a run starts in <code>auto</code> mode, Claude Code drops a bare <code>Bash</code> entry as a broad allow rule, and the classifier checks each command instead.</p>
<h3 id="turn-prompts-off-completely">Turn prompts off completely</h3>
<p><code>--permission-prompts none</code> is meant for runs where nobody can answer. It needs Claude Code v2.1.259 or later, and earlier versions reject it as an unknown option. Permission rules, <code>PermissionRequest</code> hooks and the permission mode still decide each call first. Anything left that would prompt is denied, Claude is told that nobody can approve it and that it shouldn&#39;t retry, and the run continues. Tools that need a person&#39;s answer, such as <code>AskUserQuestion</code>, are removed:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Update the dependency pins and run the tests&quot; --permission-mode auto --permission-prompts none</code></pre></div>
<p>In a plain <code>-p</code> run with no permission host, these requests are denied anyway, and the flag adds the instruction not to retry. The flag matters most in runs that do have a host, meaning an Agent SDK <code>canUseTool</code> callback or a <code>--permission-prompt-tool</code>. Without the flag, the run waits for that host to answer each request.</p>
<p>With <code>stream-json</code> output, each denial appears as a <code>permission_denied</code> system message, and the final result lists all of them in <code>permission_denials</code>. If you save that list as a CI artifact, you can see which rule to add or which request needs a closer look.</p>
<h2 id="get-output-your-pipeline-can-parse">Get output your pipeline can parse</h2>
<p><code>--output-format</code> accepts <code>text</code> (the default), <code>json</code> or <code>stream-json</code>. The <code>json</code> format returns the result along with the session ID and other metadata. To get a fixed shape, add <code>--json-schema</code>. The answer then appears in <code>structured_output</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Extract function names from auth.py&quot; \
  --output-format json \
  --json-schema '{&quot;type&quot;:&quot;object&quot;,&quot;properties&quot;:{&quot;functions&quot;:{&quot;type&quot;:&quot;array&quot;,&quot;items&quot;:{&quot;type&quot;:&quot;string&quot;}}},&quot;required&quot;:[&quot;functions&quot;]}' \
  | jq '.structured_output'</code></pre></div>
<p>If the schema is invalid, <code>claude</code> exits with an error. The <code>format</code> keyword (for example <code>&quot;format&quot;: &quot;email&quot;</code>) is accepted as an annotation but not enforced, so validate those fields yourself.</p>
<p>The JSON payload also includes <code>total_cost_usd</code> and a cost breakdown per model. Anthropic describes these as client-side estimates that can differ from your actual bill. Use them to track spending, not as billing data.</p>
<p>A review job can add a role prompt and still keep the default system prompt:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">gh pr diff &quot;$1&quot; | claude -p \
  --append-system-prompt &quot;You are a security engineer. Review for vulnerabilities.&quot; \
  --output-format json</code></pre></div>
<h3 id="fail-the-build-when-a-server-or-plugin-didnt-load">Fail the build when a server or plugin didn&#39;t load</h3>
<p>When you pass <code>--mcp-config</code>, Claude Code checks each entry at startup and skips invalid ones, such as a <code>url</code> entry with no <code>type</code>. The run then continues and exits cleanly. In a terminal you would see a warning on stderr. When a CI runner captures or redirects stderr, though, no warning is printed.</p>
<p>The skipped entries are then reported only in the <code>system/init</code> event, which you can read from <code>stream-json</code> output:</p>
<ul>
<li><code>mcp_server_errors</code> lists skipped servers (v2.1.219 or later).</li>
<li><code>plugin_errors</code> lists plugins that failed to load.</li>
</ul>
<p>Both keys are left out when there are no errors, so fail the job if either one is present and non-empty. Otherwise a review step can pass without the tool it depends on.</p>
<h2 id="keep-runs-bounded">Keep runs bounded</h2>
<p>CI jobs need limits on time and cost. The docs describe these controls:</p>
<ul>
<li><strong>Background work.</strong> After its turn, a <code>-p</code> run can stay open while background work that Claude started finishes. By default, Claude Code stops whatever is still running after 10 minutes of continuous idle waiting and drops its partial result. Some work is exempt. For example, a background command started by the main conversation keeps the run open until the command exits or hits its own time limit. <code>CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS</code> changes the 10-minute cap, and setting it to <code>0</code> removes the cap. In bare mode, no background tasks run.</li>
<li><strong>Budget.</strong> <code>--max-budget-usd</code> stops the run once estimated spend reaches the amount you set. It checks against the client-side estimate, and spend can go past the cap, so leave headroom. Once the cap is reached, Claude Code also stops any remaining background work instead of waiting for it. <code>--max-turns</code> limits the number of agentic turns and exits with an error when the limit is reached.</li>
<li><strong>Cancellation.</strong> If your runner sends SIGTERM, Claude Code exits with code 143. The current turn is left unfinished with no result, Bash process trees are terminated, and <code>SessionEnd</code> hooks run. If you want the turn to end properly, send SIGINT first.</li>
</ul>
<h2 id="multi-step-jobs-resume-by-session-id">Multi-step jobs: resume by session ID</h2>
<p>Some jobs work better in stages, such as reviewing first and summarising afterwards. Capture the session ID, then resume that session explicitly:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">session_id=$(claude -p &quot;Start a review&quot; --output-format json | jq -r '.session_id')
claude -p &quot;Continue that review&quot; --resume &quot;$session_id&quot;</code></pre></div>
<p>Use <code>--resume</code> with an ID rather than <code>--continue</code>. <code>--continue</code> loads the most recent conversation in the current directory, which may not be the one you want when several jobs share a runner. After a resume, the reported cost covers the whole conversation, earlier runs included. Those earlier totals don&#39;t count toward <code>--max-budget-usd</code>.</p>
<h2 id="a-checklist-for-headless-ci-jobs">A checklist for headless CI jobs</h2>
<ol>
<li>Use <code>claude --bare -p</code>, and load prompts, settings and MCP servers only through explicit flags.</li>
<li>Authenticate with <code>ANTHROPIC_API_KEY</code> (or your cloud provider&#39;s credentials) from the CI secret store.</li>
<li>Set <code>--permission-mode</code> yourself, starting with <code>dontAsk</code>.</li>
<li>In <code>--allowedTools</code>, use prefix rules like <code>Bash(git diff *)</code> instead of a bare <code>Bash</code>.</li>
<li>Pipe data in where you can, so Claude needs fewer tools.</li>
<li>Use <code>--output-format json</code>, add <code>--json-schema</code> when later steps depend on the output&#39;s shape, and check the exit code.</li>
<li>Fail the job on non-empty <code>mcp_server_errors</code> or <code>plugin_errors</code>, and save <code>permission_denials</code>.</li>
<li>Set <code>--max-budget-usd</code> with headroom, plus a job timeout in your CI system.</li>
<li>Pin the Claude Code version on your runners, for example with <code>claude install &lt;version&gt;</code>.</li>
</ol>
<p>The same principles apply when you run many agents instead of one. <a href="/atlas/">Atlas</a> runs each engineer as its own Claude Code session with a written role and its own working folder. It leaves production go-lives, spending, deleting data and sending anything outside to a person.</p>
<h2 id="summary">Summary</h2>
<p>Running Claude Code headless comes down to two things. Make each run independent of the machine it lands on, and make permissions an explicit allowlist instead of relying on a default. <code>--bare</code>, an explicit <code>--permission-mode</code>, narrow <code>--allowedTools</code> rules and JSON output cover most of that. The other checks catch failures that would otherwise go unnoticed: the exit code, <code>mcp_server_errors</code> and <code>permission_denials</code>.</p>
<p>Several behaviours described here depend on recent versions, including <code>--permission-prompts</code> (v2.1.259) and the full bare-mode limits (v2.1.286). Pin your version, and reread the <a href="https://code.claude.com/docs/en/headless" rel="noopener">programmatic usage docs</a> whenever you upgrade.</p>
]]></content:encoded></item>
<item><title>Claude Code Subagents: When to Split Work and How</title><link>https://bridge-terminal.com/atlas/blog/claude-code-subagents-when-to-split-work/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-subagents-when-to-split-work/</guid><pubDate>Sat, 10 Oct 2026 08:50:00 GMT</pubDate><description>When Claude Code subagents help and when they don't: writing the agent file, choosing tools and models, and catching frontmatter mistakes that fail silently.</description><category>claude-code</category><category>subagents</category><category>ai-agents</category><content:encoded><![CDATA[<ul><li>A Claude Code subagent works in its own context window with its own prompt, tools and permissions, and usually sends back only a summary, so noisy side tasks stay out of your main conversation.</li><li>Use a subagent when a task produces lots of output you won't reread, needs enforced tool limits, can be summarized on its own, or needs a reviewer that didn't write the code.</li><li>Stay in the main conversation when you're iterating step by step, several phases share a lot of context, the change is quick and targeted, or latency matters.</li><li>A fresh subagent doesn't see your conversation history, output style or auto memory, so the delegation prompt must carry every constraint it needs.</li><li>Misspelled frontmatter fields are ignored without an error; run claude --debug and search the debug log for 'does not recognize or apply' when a setting doesn't take effect.</li></ul><p>Use Claude Code subagents when a side task would fill your main conversation with output you won&#39;t look at again, when a job needs tighter tool limits than your session has, or when you want a reviewer that didn&#39;t write the code. A subagent is a Markdown file with YAML frontmatter in <code>.claude/agents/</code> or <code>~/.claude/agents/</code>. Only <code>name</code> and <code>description</code> are required. Everything else (tools, model, permissions, hooks, memory and so on) is optional, but each one is worth a deliberate choice.</p>
<p>This guide covers when splitting work pays off, how to write a definition Claude actually delegates to, and the configuration mistakes that fail without an error.</p>
<h2 id="what-a-claude-code-subagent-is">What a Claude Code subagent is</h2>
<p>A subagent runs in its own context window with its own system prompt, tool access and permissions. Claude hands it a task, it works independently, and it returns a result, usually a summary. Only that result comes back into your main conversation.</p>
<p>What matters more is what a fresh (non-fork) subagent sees. According to the docs, it starts with:</p>
<ul>
<li>its own system prompt plus basic environment details such as the working directory, not the Claude Code system prompt</li>
<li>the delegation message Claude wrote for it</li>
<li>your CLAUDE.md files, unless the definition sets <code>omitClaudeMd</code></li>
<li>a git status snapshot</li>
<li>the full content of any skills listed in its <code>skills</code> field</li>
<li>a list of <code>main</code> and the other named agents in the session</li>
</ul>
<p>The built-in Explore and Plan agents skip CLAUDE.md and the git status snapshot to keep research fast and cheap.</p>
<p>A subagent does <strong>not</strong> get your conversation history, your output style or the main conversation&#39;s auto memory. The task prompt has to carry everything. If you agreed on a constraint twenty messages ago, the subagent won&#39;t know it unless the delegation prompt repeats it.</p>
<p>Subagent requests count toward the same usage limits as your main conversation. Splitting work doesn&#39;t make it free.</p>
<h2 id="when-to-split-work-into-a-subagent">When to split work into a subagent</h2>
<p><strong>Split when:</strong></p>
<ul>
<li><strong>The output is noisy.</strong> The best-practices guide notes that a single debugging session or codebase exploration can use tens of thousands of tokens. Its advice: prompt &quot;use subagents to investigate X&quot; so the reading happens in a separate context and only summaries come back.</li>
<li><strong>You want enforced limits.</strong> A read-only reviewer, or a database agent that may only run <code>SELECT</code>, should have those limits in configuration, not just in the prompt.</li>
<li><strong>You want an independent check.</strong> A reviewer in a fresh context sees only the diff and the criteria you give it, not the reasoning that produced the change.</li>
<li><strong>The task is self-contained.</strong> If it fits in one prompt and can be answered with a summary, it&#39;s a good candidate.</li>
<li><strong>You keep needing the same kind of worker.</strong> That&#39;s the point to save a definition instead of re-typing the instructions.</li>
</ul>
<p><strong>Keep it in the main conversation when:</strong></p>
<ul>
<li>you&#39;re iterating back and forth and each step depends on the last</li>
<li>several phases, such as planning, implementation and testing, share a lot of context</li>
<li>the change is quick and targeted</li>
<li>latency matters, because a fresh subagent may need time to gather context</li>
</ul>
<p>If you want a reusable prompt that runs <em>in</em> your main context, use a skill instead. For a quick question about something already in the conversation, use <code>/btw</code>: it sees your context but has no tool access, and its answer isn&#39;t added to history.</p>
<h2 id="start-with-the-built-in-subagents">Start with the built-in subagents</h2>
<p>Claude Code ships with:</p>
<ul>
<li><strong>Explore</strong>: fast, read-only codebase search. Claude picks a thoroughness level of quick, medium or very thorough.</li>
<li><strong>Plan</strong>: a read-only research agent used in plan mode.</li>
<li><strong>General-purpose</strong>: every tool available to subagents, for multi-step tasks that need both exploration and action.</li>
</ul>
<p>Explore and Plan are denied Write and Edit. A plain prompt such as &quot;use subagents to investigate how token refresh works&quot; often needs no custom definition. Write your own when you need a specific role, tool set or model.</p>
<h2 id="how-to-define-a-subagent">How to define a subagent</h2>
<h3 id="where-the-file-lives">Where the file lives</h3>
<p>When names collide, the higher-priority location wins:</p>
<div class="table"><table><thead><tr><th>Location</th><th>Scope</th><th>Priority</th></tr></thead><tbody><tr><td>Managed settings</td><td>Organization-wide</td><td>1 (highest)</td></tr><tr><td><code>--agents</code> CLI flag</td><td>Current session</td><td>2</td></tr><tr><td><code>.claude/agents/</code></td><td>Current project</td><td>3</td></tr><tr><td><code>~/.claude/agents/</code></td><td>All your projects</td><td>4</td></tr><tr><td>Plugin <code>agents/</code> directory</td><td>Where the plugin is enabled</td><td>5 (lowest)</td></tr></tbody></table></div>
<p>Project agents are found by walking up from the current working directory to the repository root, and agent directories are scanned recursively, so subfolders such as <code>agents/review/</code> work. If two files in the same directory tree declare the same <code>name</code>, only one loads, chosen by filesystem read order. <code>/doctor</code> reports these duplicates. Check project agents into version control so your team shares them.</p>
<h3 id="a-minimal-useful-definition">A minimal, useful definition</h3>
<p>This is the read-only code reviewer from the official examples, shortened (the full version also has a review checklist). It shows three habits: a narrow role, a restricted tool list and a prompt that says exactly what to output.</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown">---
name: code-reviewer
description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code.
tools: Read, Grep, Glob, Bash
model: inherit
---

You are a senior code reviewer ensuring high standards of code quality and security.

When invoked:
1. Run git diff to see recent changes
2. Focus on modified files
3. Begin review immediately

Provide feedback organized by priority:
- Critical issues (must fix)
- Warnings (should fix)
- Suggestions (consider improving)

Include specific examples of how to fix issues.</code></pre></div>
<p>Claude Code watches the agent directories and picks up edits within a few seconds. You need a restart in three cases: after creating the first agent file in an <code>agents</code> directory that didn&#39;t exist when the session started, after changing agents in a directory added with <code>--add-dir</code> or <code>/add-dir</code>, and in sessions started with <code>--disable-slash-commands</code>.</p>
<h3 id="write-the-description-for-routing">Write the description for routing</h3>
<p>Claude reads <code>description</code> to decide when to delegate, so write it as a routing rule:</p>
<ul>
<li>Make it specific enough to point to one subagent. Overlapping descriptions lead to the wrong pick.</li>
<li>Add a phrase like &quot;use proactively&quot; if you want Claude to delegate without being asked.</li>
<li>Keep it short and put detail in the body, which loads only when the subagent runs. Claude Code warns at startup when the combined descriptions of your non-built-in subagents exceed 15,000 tokens.</li>
</ul>
<h3 id="restrict-tools-deliberately">Restrict tools deliberately</h3>
<p>If you leave out <code>tools</code>, the subagent inherits every tool available to subagents. <code>tools</code> is an allowlist and <code>disallowedTools</code> a denylist:</p>
<div class="code"><span class="lang">yaml</span><pre><code class="language-yaml">---
name: no-writes
description: Inherits the available tools except file writes
disallowedTools: Write, Edit
---</code></pre></div>
<p>If both are set, <code>disallowedTools</code> is applied first and <code>tools</code> is resolved against what remains. Two details:</p>
<ul>
<li><code>mcp__&lt;server&gt;</code> grants or removes every tool from one MCP server. In <code>disallowedTools</code>, <code>mcp__*</code> removes all MCP tools.</li>
<li><code>Bash(git push *)</code> in <code>disallowedTools</code> removes the <strong>whole</strong> Bash tool, not just that command. To keep Bash and block specific commands, add a deny rule such as <code>Bash(git push *)</code> to <code>permissions.deny</code> in your settings. For finer, conditional checks, use a <code>PreToolUse</code> hook.</li>
</ul>
<p>The docs&#39; <code>db-reader</code> example takes the hook route. The agent gets Bash, and a <code>PreToolUse</code> hook in its frontmatter runs a script that reads the hook input as JSON from stdin and extracts the command. If it contains SQL write keywords such as <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>, <code>DROP</code> or <code>ALTER</code>, the script exits with code 2 and the command is blocked. On macOS and Linux the script must be executable, or the hook fails instead of blocking anything. The prompt tells the agent it is read-only; the hook is the backstop.</p>
<h3 id="choose-a-model-and-effort-level-per-role">Choose a model and effort level per role</h3>
<p><code>model</code> accepts <code>sonnet</code>, <code>opus</code>, <code>haiku</code>, <code>fable</code>, a full model ID, or <code>inherit</code>. The docs suggest controlling costs by routing tasks to faster, lower-cost models like Haiku, which suits narrow work such as searching or summarizing logs. Reviewers and anything that reasons about design usually deserve a stronger model.</p>
<p>The model is resolved in this order: a <code>model</code> Claude passes for that invocation, then the frontmatter value, then the <code>CLAUDE_CODE_SUBAGENT_MODEL</code> environment variable, then the main conversation&#39;s model. To force one model on every subagent (Claude Code v2.1.257 or later), set both variables:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;env&quot;: {
    &quot;CLAUDE_CODE_SUBAGENT_MODEL&quot;: &quot;haiku&quot;,
    &quot;CLAUDE_CODE_SUBAGENT_MODEL_FORCE&quot;: &quot;1&quot;
  }
}</code></pre></div>
<p>The <code>effort</code> field takes <code>low</code>, <code>medium</code>, <code>high</code>, <code>xhigh</code> or <code>max</code>; which levels are available depends on the model.</p>
<h3 id="other-fields-worth-knowing">Other fields worth knowing</h3>
<ul>
<li><code>permissionMode</code>: ignored when the main session is in <code>bypassPermissions</code>, <code>acceptEdits</code> or auto mode; the subagent runs in that mode instead.</li>
<li><code>maxTurns</code>: caps the agent&#39;s turns. At the cap, output is returned marked as partial and Claude can resume the agent.</li>
<li><code>skills</code>: preloads the full content of listed skills (up to 32 names). Without it, the subagent can still find and invoke skills while it works.</li>
<li><code>memory</code>: <code>user</code>, <code>project</code> or <code>local</code> persistent memory. The docs recommend <code>project</code> as the default because it can be shared through version control.</li>
<li><code>isolation: worktree</code>: runs the subagent in a temporary git worktree; it is removed automatically if the subagent makes no changes.</li>
<li><code>background: true</code>: keeps the subagent in the background even when Claude asks for the foreground.</li>
</ul>
<h2 id="how-to-invoke-a-subagent">How to invoke a subagent</h2>
<ul>
<li><strong>Automatically:</strong> Claude matches the task to a description.</li>
<li><strong>By name:</strong> <code>Use the code-reviewer subagent to review the auth changes</code></li>
<li><strong>@-mention:</strong> <code>@&quot;code-reviewer (agent)&quot; look at the auth changes</code>, or type <code>@agent-&lt;name&gt;</code>.</li>
<li><strong>Whole session:</strong> <code>claude --agent code-reviewer</code>. Unless its prompt is empty, the agent&#39;s prompt replaces the default Claude Code system prompt.</li>
<li><strong>Session-only:</strong> pass JSON with <code>--agents</code>, where <code>prompt</code> replaces the Markdown body, for example <code>claude --agents &#39;{&quot;code-reviewer&quot;: {&quot;description&quot;: &quot;...&quot;, &quot;prompt&quot;: &quot;...&quot;, &quot;tools&quot;: [&quot;Read&quot;, &quot;Grep&quot;], &quot;model&quot;: &quot;sonnet&quot;}}&#39;</code>.</li>
</ul>
<p>When a background subagent needs permission for a tool, the prompt appears in your main session with the subagent&#39;s name; Esc denies that one call without stopping it. Press Ctrl+B to move a running task to the background. Resumed subagents keep their full history, except Explore and Plan, which are one-shot and can&#39;t be resumed.</p>
<h3 id="forks-are-a-different-tool">Forks are a different tool</h3>
<p>A fork inherits the whole conversation, system prompt, tools and model. Its first request reuses the main session&#39;s prompt cache, so it costs less than a fresh subagent when the task needs the same context. Start one with <code>/subtask &lt;task&gt;</code> (on v2.1.161 through v2.1.211 the command is <code>/fork</code>). Use a fork when the side task needs everything you&#39;ve discussed; use a defined subagent for a clean slate and a specific role.</p>
<h2 id="add-an-adversarial-reviewer">Add an adversarial reviewer</h2>
<p>The best-practices guide recommends a review step before you count unattended work as done. For a plain bug check of the current diff, the bundled <code>/code-review</code> skill runs in a fresh subagent. To check against a plan, name the work, the standard and what counts as a finding:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">Use a subagent to review the rate limiter diff against PLAN.md. Check that
every requirement is implemented, the listed edge cases have tests, and
nothing outside the task's scope changed. Report gaps, not style preferences.</code></pre></div>
<p>The guide also warns that a reviewer asked to find gaps will usually report some even when the work is sound. Chasing every finding leads to extra abstraction, defensive code and tests for cases that can&#39;t happen. Tell the reviewer to flag only gaps that affect correctness or the stated requirements.</p>
<h2 id="mistakes-that-fail-silently">Mistakes that fail silently</h2>
<ul>
<li><strong>Typos in frontmatter.</strong> Unrecognized field names are ignored without an error. Run <code>claude --debug</code> and search the session&#39;s log in <code>~/.claude/debug/</code> for <code>does not recognize or apply</code>.</li>
<li><strong>Missing fields or broken YAML.</strong> A file with no <code>name</code> is treated as documentation and never loads. A <code>name</code> with no <code>description</code>, or YAML that doesn&#39;t parse, is skipped with only a note in the debug log.</li>
<li><strong>Shorthand numbers.</strong> In <code>autoCompactWindow</code>, <code>200k</code> is read as 200, outside the allowed range of 100,000 to 1,000,000. Write <code>200000</code>.</li>
<li><strong>Assuming shared context.</strong> Repeat key constraints in the delegation prompt, including CLAUDE.md rules if the agent sets <code>omitClaudeMd</code>.</li>
<li><strong>Spawning without limits.</strong> By default subagents can nest up to three layers below the main conversation, and spawning fails once 20 are running. Change these with <code>CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH</code> and <code>CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS</code>. Each extra subagent adds to token spend.</li>
</ul>
<h2 id="subagents-versus-separate-sessions">Subagents versus separate sessions</h2>
<p>Subagents are workers inside one session; their results come back to a single orchestrator&#39;s context. For longer-lived roles, the next step is separate Claude Code sessions, each with its own working folder. <a href="/atlas/">Atlas</a> works this way: each of its engineers is its own Claude Code session with a written role, its own working folder, and a model and effort level suited to the role. Either way, give each worker one clear job, the tools it needs and a defined deliverable.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Start with the built-in Explore agent and plain &quot;use subagents to investigate…&quot; prompts. Write a custom definition when you keep needing the same role. Keep the description short and specific, restrict tools in configuration rather than in the prompt, pick a model that fits the job, and put everything the agent needs in the task prompt. Add a fresh-context reviewer before trusting long unattended runs, and check the <code>claude --debug</code> log when a field doesn&#39;t seem to take effect.</p>
]]></content:encoded></item>
<item><title>Claude Code Hooks as Guardrails: Block, Format, Test</title><link>https://bridge-terminal.com/atlas/blog/claude-code-hooks-guardrails/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-hooks-guardrails/</guid><pubDate>Fri, 09 Oct 2026 13:50:00 GMT</pubDate><description>Use Claude Code hooks to block risky shell commands, protect files, format every edit and keep Claude working until tests pass, with configs you can copy.</description><category>claude-code</category><category>hooks</category><category>ai-agents</category><category>guardrails</category><content:encoded><![CDATA[<ul><li>Claude Code hooks are shell commands that Claude Code runs at fixed points in a session, so a rule like &quot;format every edited file&quot; runs every time instead of depending on the model to remember it.</li><li>A PreToolUse hook can block risky shell commands such as rm -rf or force pushes, and edits to files like .env. Its deny holds even in bypassPermissions mode.</li><li>A PostToolUse hook with an Edit|Write matcher can run a formatter such as Prettier on each file Claude edits. It can't undo the edit.</li><li>A Stop hook can keep Claude working while tests fail. Check stop_hook_active so it doesn't loop, and know that Claude Code overrides a Stop hook after eight blocks in a row.</li><li>Policy hooks must exit 2 (exit 1 enforces nothing) or return a JSON decision. Add onFailure: &quot;block&quot; so a broken hook doesn't let the action through, and keep absolute bans in permission rules.</li></ul><p>Claude Code hooks are shell commands that Claude Code runs at fixed points in a session, such as before a tool call, after it, and when Claude finishes responding. They turn &quot;please always run the formatter&quot; from a request the model may forget into a rule that always runs. Three hooks cover most guardrail needs. A <code>PreToolUse</code> hook blocks risky commands and edits. A <code>PostToolUse</code> hook formats each edited file. A <code>Stop</code> hook keeps Claude working while tests fail.</p>
<p>This guide sets up all three, explains the exit codes and JSON that control them, and covers the mistakes that make a guardrail fail without telling you.</p>
<h2 id="how-claude-code-hooks-work">How Claude Code hooks work</h2>
<p>You define a hook in a settings file under the <code>hooks</code> key. It has three levels: the event, a matcher group, and one or more handlers. The file you put it in decides who it applies to:</p>
<ul>
<li><code>~/.claude/settings.json</code>: all your projects, only on your machine.</li>
<li><code>.claude/settings.json</code>: one project, and you can commit it to the repo. Team guardrails go here.</li>
<li><code>.claude/settings.local.json</code>: one project, not shared.</li>
<li>Managed policy settings: organisation-wide, controlled by admins.</li>
</ul>
<p>When an event fires, Claude Code sends JSON to your command&#39;s stdin. For a Bash call, a <code>PreToolUse</code> hook gets <code>tool_name</code> and <code>tool_input</code> (<code>tool_input.command</code> holds the shell command). For <code>Edit</code> and <code>Write</code>, <code>tool_input.file_path</code> holds the target file as an absolute path.</p>
<p>Your command answers with its exit code:</p>
<ul>
<li><strong>Exit 0</strong>: no objection. On <code>PreToolUse</code> this does <em>not</em> approve the call. The normal permission flow still applies.</li>
<li><strong>Exit 2</strong>: block. Write the reason to stderr. On <code>PreToolUse</code>, Claude gets it as feedback on why the call was blocked. On <code>Stop</code>, Claude gets it as the reason to keep working.</li>
<li><strong>Any other code</strong>: unless stdout holds valid JSON that decides the outcome, this is a non-blocking error and the action goes ahead.</li>
</ul>
<p>The docs are explicit that exit 1, the usual Unix failure code, enforces nothing. If a hook enforces a policy, it must exit 2 or return a JSON decision.</p>
<p>All hooks that match an event run in parallel, and each one runs to completion even if another denies. For <code>PreToolUse</code> decisions the most restrictive result wins, in the order <code>deny</code>, <code>defer</code>, <code>ask</code>, <code>allow</code>. (<code>defer</code> only works in non-interactive <code>-p</code> mode.)</p>
<h2 id="block-risky-shell-commands-with-pretooluse">Block risky shell commands with PreToolUse</h2>
<p>The docs show the basic pattern: read stdin, pull out the command, and exit 2 if it matches something you never want run. Here it is, extended to a short deny list. Save it as <code>.claude/hooks/block-risky-bash.sh</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo &quot;$INPUT&quot; | jq -r '.tool_input.command')

for pattern in &quot;rm -rf&quot; &quot;git push --force&quot; &quot;drop table&quot;; do
  if echo &quot;$COMMAND&quot; | grep -qi -- &quot;$pattern&quot;; then
    echo &quot;Blocked: '$pattern' is not allowed in this repo. Ask the user to run it.&quot; &gt;&amp;2
    exit 2
  fi
done

exit 0</code></pre></div>
<p>Make it executable with <code>chmod +x .claude/hooks/block-risky-bash.sh</code>, then register it:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;\&quot;$CLAUDE_PROJECT_DIR\&quot;/.claude/hooks/block-risky-bash.sh&quot;
          }
        ]
      }
    ]
  }
}</code></pre></div>
<p><code>$CLAUDE_PROJECT_DIR</code> is the project root where the session started, so the path still works after Claude changes directory. The scripts here use <code>jq</code> to parse JSON, so install it first.</p>
<p>Plain substring matching is easy to get around: <code>rm -r -f</code> or <code>git push -f</code> slip past this list. Treat it as a first line of defence and keep hard bans in permission rules (see below).</p>
<p>Write the stderr message for the model as well as for you. Claude reads it and adjusts, so &quot;ask the user to run it&quot; gives it more to work with than a bare &quot;denied&quot;.</p>
<h3 id="deny-with-json-when-you-want-structured-output">Deny with JSON when you want structured output</h3>
<p>Instead of exiting 2, you can exit 0 and print a decision on stdout:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;hookSpecificOutput&quot;: {
    &quot;hookEventName&quot;: &quot;PreToolUse&quot;,
    &quot;permissionDecision&quot;: &quot;deny&quot;,
    &quot;permissionDecisionReason&quot;: &quot;Database writes are not allowed&quot;
  }
}</code></pre></div>
<p><code>permissionDecision</code> must sit inside <code>hookSpecificOutput</code>. If you put it at the top level, the JSON still parses, but Claude Code ignores the field without reporting an error. The docs advise picking one method per hook: exit 2 with stderr, or exit 0 with JSON.</p>
<h3 id="why-a-pretooluse-deny-holds-in-every-mode">Why a PreToolUse deny holds in every mode</h3>
<p><code>PreToolUse</code> hooks fire before any permission-mode check. A deny blocks the tool even in <code>bypassPermissions</code> mode or with <code>--dangerously-skip-permissions</code>. This matters most in unattended runs. If you run Claude Code from scripts or pipelines, pair these hooks with the permission settings covered in <a href="/atlas/blog/claude-code-headless-ci-permissions/">Claude Code Headless in CI</a>.</p>
<p>The docs list one exception. A mod (a plugin whose code runs inside Claude Code) that handles <code>tool.check</code> can approve a call your <code>PreToolUse</code> hook blocked, unless the hook is in managed settings. Check what your installed mods do before you rely on a hook alone.</p>
<p>It does not work the other way. A hook that returns <code>&quot;allow&quot;</code> does not bypass deny rules in settings. Hooks in settings files can tighten restrictions but not loosen them past your permission rules.</p>
<h2 id="protect-sensitive-files-from-edits">Protect sensitive files from edits</h2>
<p>The same approach works for file edits. The Claude Code hooks guide uses this script to block changes to <code>.env</code>, <code>package-lock.json</code> and anything in <code>.git/</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">#!/bin/bash
# protect-files.sh

INPUT=$(cat)
FILE_PATH=$(echo &quot;$INPUT&quot; | jq -r '.tool_input.file_path // empty')

# Normalize Windows backslash separators so the patterns below match
FILE_PATH=&quot;${FILE_PATH//\\//}&quot;

PROTECTED_PATTERNS=(&quot;.env&quot; &quot;package-lock.json&quot; &quot;.git/&quot;)

for pattern in &quot;${PROTECTED_PATTERNS[@]}&quot;; do
  if [[ &quot;$FILE_PATH&quot; == *&quot;$pattern&quot;* ]]; then
    echo &quot;Blocked: $FILE_PATH matches protected pattern '$pattern'&quot; &gt;&amp;2
    exit 2
  fi
done

exit 0</code></pre></div>
<p>Register it on <code>PreToolUse</code> with the matcher <code>Edit|Write</code>, the same way as the Bash hook. Claude can also change files from the shell, which this matcher does not see. The docs suggest two ways to catch every change: also match <code>Bash|PowerShell</code> and have the script list modified and untracked files with <code>git status --porcelain</code>, or add a <code>Stop</code> hook that scans the working tree once per turn.</p>
<h2 id="enforce-formatting-after-every-edit">Enforce formatting after every edit</h2>
<p>Formatting belongs in a <code>PostToolUse</code> hook. It runs after the edit lands, so it can&#39;t prevent the edit, but it keeps the file tidy. This is the documented Prettier hook for <code>.claude/settings.json</code>:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;hooks&quot;: {
    &quot;PostToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Edit|Write&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;jq -r '.tool_input.file_path' | xargs npx prettier --write&quot;
          }
        ]
      }
    ]
  }
}</code></pre></div>
<p>To use a different formatter, put its command after <code>xargs</code>. Two things to know:</p>
<ul>
<li><code>PostToolUse</code> cannot undo an action, because the tool has already run.</li>
<li>To push back after an edit, for example when a linter fails, return <code>{&quot;decision&quot;: &quot;block&quot;, &quot;reason&quot;: &quot;...&quot;}</code>. The reason goes to Claude to read.</li>
</ul>
<p>If you want a specific file reformatted however it changes, including by a Bash command, the docs point to the <code>FileChanged</code> event instead.</p>
<h2 id="make-tests-pass-before-claude-stops">Make tests pass before Claude stops</h2>
<p>A <code>Stop</code> hook fires whenever Claude finishes responding. If it blocks, Claude keeps working, and your exit-2 stderr (or JSON <code>reason</code>) tells it why. That makes it a good place for a test gate:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">#!/bin/bash
INPUT=$(cat)
if [ &quot;$(echo &quot;$INPUT&quot; | jq -r '.stop_hook_active')&quot; = &quot;true&quot; ]; then
  exit 0  # Allow Claude to stop
fi

if ! OUTPUT=$(npm test 2&gt;&amp;1); then
  echo &quot;Tests are failing. Fix them before finishing:&quot; &gt;&amp;2
  echo &quot;$OUTPUT&quot; | tail -n 40 &gt;&amp;2
  exit 2
fi

exit 0</code></pre></div>
<p>Save it as <code>.claude/hooks/require-tests.sh</code> and register it under <code>&quot;Stop&quot;</code>. <code>Stop</code> does not support matchers, so leave the matcher out:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;\&quot;$CLAUDE_PROJECT_DIR\&quot;/.claude/hooks/require-tests.sh&quot;
          }
        ]
      }
    ]
  }
}</code></pre></div>
<p>The <code>stop_hook_active</code> check matters. The field is <code>true</code> when Claude is already continuing because a stop hook blocked. Claude Code also has a hard cap: after a Stop hook blocks eight times in a row with no tool call from Claude in between, Claude Code overrides it and ends the turn. You can raise the cap with the <code>CLAUDE_CODE_STOP_HOOK_BLOCK_CAP</code> environment variable. With the early exit on <code>stop_hook_active</code>, this script blocks once per stop attempt rather than looping on a test that can never pass.</p>
<p>Two more points:</p>
<ul>
<li><code>Stop</code> fires at the end of every response, not only when a task is complete. Keep the test command fast, or run only the affected suite.</li>
<li>Command hooks time out after 10 minutes by default. Set <code>timeout</code> in seconds if you need a different limit.</li>
</ul>
<p>The docs also show an <code>agent</code>-type Stop hook that runs the test suite itself. Agent hooks are marked experimental, and the docs recommend command hooks for production workflows.</p>
<h2 id="make-guardrails-fail-closed">Make guardrails fail closed</h2>
<p>By default, if a hook fails (it can&#39;t start, times out, or exits with a code other than 0 or 2 without a JSON decision), Claude Code reports a non-blocking error and the action goes ahead. On <code>PreToolUse</code>, a hook that times out does not block the tool call. That&#39;s fine for formatting but wrong for a policy hook. Add <code>&quot;onFailure&quot;: &quot;block&quot;</code> to the hook so a failure does what exit 2 would do on that event. It needs Claude Code v2.1.295 or later. It has no effect on <code>Stop</code> hooks or on async hooks.</p>
<p>Hooks also run alongside Claude Code&#39;s permission system, not in place of it. The docs describe the <code>if</code> filter (for example <code>&quot;if&quot;: &quot;Bash(git *)&quot;</code>, which limits when a hook runs) as best-effort, and recommend the permission system for a hard allow or deny. Use both:</p>
<ul>
<li>Put absolute bans in permission deny rules.</li>
<li>Use <code>PreToolUse</code> hooks for checks that need logic, such as patterns, file paths or project state.</li>
<li>Use <code>PostToolUse</code> and <code>Stop</code> hooks to enforce quality.</li>
</ul>
<p>Rules you&#39;d like Claude to follow but that don&#39;t need enforcing belong in your project instructions file. <a href="/atlas/blog/claude-md-best-practices-project-instructions/">CLAUDE.md Best Practices</a> covers how to write them.</p>
<h2 id="test-and-debug-your-hooks">Test and debug your hooks</h2>
<ul>
<li>Run <code>/hooks</code> inside Claude Code to see every configured hook, grouped by event.</li>
<li>Test a script without Claude by piping sample JSON into it:</li>
</ul>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">echo '{&quot;tool_name&quot;:&quot;Bash&quot;,&quot;tool_input&quot;:{&quot;command&quot;:&quot;ls&quot;}}' | ./my-hook.sh
echo $?  # Check the exit code</code></pre></div>
<ul>
<li>If you see &quot;command not found&quot;, use <code>${CLAUDE_PROJECT_DIR}</code> or absolute paths. If you see &quot;jq: command not found&quot;, install <code>jq</code>. If the script doesn&#39;t run at all, make it executable with <code>chmod +x</code>.</li>
<li>If your JSON has no effect and no error appears, check two things. Your shell profile may be printing text before the <code>{</code>, so Claude Code reads the output as plain text. Or a field may be at the wrong level. Start <code>claude --debug</code> and search the debug log.</li>
<li>Edits to hooks in settings files are normally picked up automatically. If they don&#39;t show up in <code>/hooks</code> after a few seconds, restart the session.</li>
</ul>
<p>Commit <code>.claude/settings.json</code> and <code>.claude/hooks/</code> together, so everyone on the team and every CI run gets the same guardrails.</p>
<h2 id="where-to-start">Where to start</h2>
<p>Hooks are the part of Claude Code that doesn&#39;t rely on the model remembering anything. Start with a <code>PreToolUse</code> deny list for commands and files you never want touched, add a <code>PostToolUse</code> formatter, then a <code>Stop</code> hook that runs your tests and respects <code>stop_hook_active</code>. Make policy hooks exit 2, set <code>onFailure: &quot;block&quot;</code> on your <code>PreToolUse</code> guardrails, and keep absolute bans in permission rules. The same idea applies beyond one session: on <a href="/atlas/">Atlas</a>, each engineer runs as its own Claude Code session, and completion is refused until the work is on main or queued for the landing train and the product&#39;s rules pass.</p>
]]></content:encoded></item>
<item><title>Claude Code vs Codex for Teams: A Workflow Comparison</title><link>https://bridge-terminal.com/atlas/blog/claude-code-vs-codex-team-workflows/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-vs-codex-team-workflows/</guid><pubDate>Thu, 08 Oct 2026 13:40:00 GMT</pubDate><description>Claude Code vs OpenAI Codex for engineering teams: instruction files, permissions, hooks, CI, cloud tasks and PR review compared, and when to pick which tool.</description><category>claude-code</category><category>codex</category><category>ai-agents</category><category>engineering-teams</category><content:encoded><![CDATA[<ul><li>Claude Code and OpenAI Codex now offer much the same set of features: an instruction file, project config, hooks, subagents, MCP, a non-interactive mode, cloud tasks and PR review. The differences are in the details.</li><li>Claude Code sets safety with allow, ask and deny rules plus permission modes. Codex sets it with two settings: a sandbox that limits what the agent can reach and an approval policy that decides when it asks.</li><li>Codex reads project config and hooks only from trusted projects, and hooks must be reviewed first. Claude Code shares a committed .claude/settings.json with everyone in the project.</li><li>Codex is included with ChatGPT plans from Free upwards. An API key works for the CLI and IDE extension, but not for cloud features like GitHub code review.</li><li>Neither tool gives you a team process for who owns a task and what 'done' means. Agree on that, along with a shared permissions policy, before you run more agents.</li></ul><p>Claude Code and OpenAI Codex do the same core job. They read a repository, edit files, run commands and propose changes. Over the past year their feature lists have grown closer together. Both now have an instruction file, project-level config, hooks, subagents, MCP support, a non-interactive mode for scripts, cloud tasks and pull request review. So the choice between them comes down to how each one handles safety, sharing settings, delegation and access, more than to any feature only one of them has.</p>
<p>This comparison covers those workflow questions. Model quality changes with every release, so it isn&#39;t covered here.</p>
<h2 id="claude-code-vs-codex-at-a-glance">Claude Code vs Codex at a glance</h2>
<div class="table"><table><thead><tr><th>Workflow concern</th><th>Claude Code</th><th>OpenAI Codex</th></tr></thead><tbody><tr><td>Local interfaces</td><td>Terminal (<code>claude</code>), VS Code and JetBrains, desktop app</td><td>Terminal (<code>codex</code>), IDE extension for VS Code and compatible editors, desktop app</td></tr><tr><td>Project instructions</td><td><code>CLAUDE.md</code> (can read an existing <code>AGENTS.md</code> instead)</td><td><code>AGENTS.md</code>, with <code>AGENTS.override.md</code> for overrides</td></tr><tr><td>Config</td><td><code>.claude/settings.json</code> (shared), <code>.claude/settings.local.json</code>, <code>~/.claude/settings.json</code></td><td><code>~/.codex/config.toml</code>, plus <code>.codex/config.toml</code> in trusted projects</td></tr><tr><td>Non-interactive use</td><td><code>claude -p &quot;&lt;prompt&gt;&quot;</code></td><td><code>codex exec &quot;&lt;prompt&gt;&quot;</code></td></tr><tr><td>Safety model</td><td>Allow/ask/deny rules plus permission modes</td><td>Sandbox mode plus approval policy</td></tr><tr><td>Extending behaviour</td><td>Hooks, subagents, skills, MCP, plugins</td><td>Hooks, custom agents, skills, MCP, plugins</td></tr><tr><td>Cloud work</td><td>Cloud sessions (web, mobile, desktop app, <code>claude --cloud</code>)</td><td>Codex Cloud tasks</td></tr><tr><td>PR review</td><td>GitHub Actions (<code>@claude</code>), Code Review</td><td><code>@codex review</code>, automatic reviews</td></tr><tr><td>Access</td><td>Claude subscription or Anthropic Console account; CLI also supports third-party providers</td><td>ChatGPT plans (Free, Go, Plus, Pro, Business, Edu, Enterprise) or an API key</td></tr></tbody></table></div>
<p>Both tools change quickly. Check the official docs (linked at the end) before you standardise on a flag or file layout.</p>
<h2 id="getting-both-tools-running">Getting both tools running</h2>
<p>Anthropic recommends Claude Code&#39;s native installer. On macOS, Linux and WSL:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">curl -fsSL https://claude.ai/install.sh | bash
claude</code></pre></div>
<p>Homebrew (<code>brew install --cask claude-code</code>) and WinGet also work, but those installs don&#39;t update automatically.</p>
<p>You can install Codex with npm, Homebrew (<code>brew install --cask codex</code>) or an install script:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">npm install -g @openai/codex
codex</code></pre></div>
<p>Each tool asks you to sign in the first time it runs. For a fair trial, give both the same three or four real tickets from your backlog: one bug fix, one small feature, one refactor and one task that needs the test suite. Then compare the diffs, how often you had to step in, and how much reviewer time each change needed.</p>
<h2 id="project-instructions-claude-md-vs-agents-md">Project instructions: CLAUDE.md vs AGENTS.md</h2>
<p>Every team ends up writing down the same things: how to run tests, which directories not to touch, naming conventions and commit format.</p>
<ul>
<li><strong>Claude Code</strong> reads <code>CLAUDE.md</code> at the start of every session. You can have one at the project root, one for your own use in <code>~/.claude/</code>, and more in subdirectories. If the repository already has an <code>AGENTS.md</code>, Claude Code can read that instead. The docs point out that <code>CLAUDE.md</code> is context, not enforced configuration. To block an action whatever the model decides, use a hook.</li>
<li><strong>Codex</strong> reads <code>AGENTS.md</code> from <code>~/.codex</code>, then from each directory between the project root and your current directory, with closer files taking precedence. An <code>AGENTS.override.md</code> replaces the regular file in its directory. The combined files are capped at 32 KiB by default.</li>
</ul>
<p>Because Claude Code can read <code>AGENTS.md</code>, a team that uses both tools can keep a single instruction file. Treat it like code: review changes in pull requests, keep it short, and delete rules the agent already follows without being told.</p>
<h2 id="permissions-and-safety">Permissions and safety</h2>
<p>This is where the two tools feel most different.</p>
<h3 id="claude-code-rules-and-modes">Claude Code: rules and modes</h3>
<p>Claude Code has allow, ask and deny rules for tools and command patterns. Settings can be checked into version control so every developer shares them, and each developer can still add their own. On top of the rules sits a permission mode. In <code>default</code> (Manual) mode it asks before most actions except reads. <code>acceptEdits</code> allows file edits and common filesystem commands without asking. <code>plan</code> reads and explores but doesn&#39;t edit source code. In auto mode, a second model (a classifier) reviews actions instead of you. From Claude Code v2.1.283, auto mode is the starting mode for interactive terminal and VS Code sessions, so if your team wants a different default, set it explicitly.</p>
<h3 id="codex-sandbox-plus-approval-policy">Codex: sandbox plus approval policy</h3>
<p>Codex separates two questions. The <strong>sandbox</strong> (<code>sandbox_mode</code>) controls what the agent can technically reach: <code>read-only</code>, <code>workspace-write</code> or <code>danger-full-access</code>. The <strong>approval policy</strong> (<code>approval_policy</code>) controls when it stops to ask: <code>on-request</code> or <code>never</code>. The docs give this as the low-friction preset for local work:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex --sandbox workspace-write --ask-for-approval on-request</code></pre></div>
<p>A separate setting, <code>approvals_reviewer</code>, can send eligible prompts to a reviewer agent instead of you. All of these can be set in <code>config.toml</code> instead of on the command line.</p>
<h3 id="what-to-standardise">What to standardise</h3>
<p>Whichever tool you pick, write down a team default and commit it:</p>
<ul>
<li>Which commands may run without asking (tests, linters, type checks).</li>
<li>Whether network access is allowed during agent runs.</li>
<li>What is never allowed (production deploys, secret files, force pushes).</li>
</ul>
<p>If every developer configures this differently, nobody can say afterwards what an agent was allowed to do.</p>
<h2 id="shared-configuration-and-automation">Shared configuration and automation</h2>
<p>Both tools have a non-interactive mode for scripts and CI:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude -p &quot;Summarise the failing tests in this branch and suggest a fix&quot;
codex exec &quot;Summarise the failing tests in this branch and suggest a fix&quot;</code></pre></div>
<p><code>codex exec</code> runs in a read-only sandbox by default. Add <code>--sandbox workspace-write</code> if the job needs to edit files. For GitHub workflows, OpenAI recommends its Codex GitHub Action over installing the CLI in a shell step.</p>
<p>The building blocks for shared behaviour are now similar:</p>
<ul>
<li><strong>Hooks.</strong> Claude Code hooks run commands at lifecycle events such as <code>PreToolUse</code> and <code>PostToolUse</code>, and a <code>PreToolUse</code> hook can block a tool call. Codex has hooks too, with events such as <code>PreToolUse</code>, <code>PermissionRequest</code> and <code>Stop</code>. They can be set at user or project level. Codex runs non-managed hooks only after you have reviewed and trusted them with <code>/hooks</code>.</li>
<li><strong>Subagents.</strong> Claude Code subagents are Markdown files in <code>.claude/agents/</code>, each with its own instructions, tool limits and context window. Codex custom agents are TOML files in <code>.codex/agents/</code>, each with its own instructions, model and reasoning effort. They inherit sandbox and approval settings from the parent session.</li>
<li><strong>Skills.</strong> Both tools package repeatable workflows as skills. In Claude Code, project skills live in <code>.claude/skills/</code>, and the older <code>.claude/commands/</code> files still work.</li>
<li><strong>MCP.</strong> Both connect to external tools and data through Model Context Protocol servers, so an issue tracker or docs server can serve both.</li>
</ul>
<p>One practical difference is how project config is trusted. Claude Code&#39;s <code>.claude/settings.json</code> is meant to be shared with everyone in the project. Codex loads a project&#39;s <code>.codex/</code> layers (config, hooks, rules) only after the project is marked as trusted. Codex profiles let you switch between named sets of settings with <code>--profile</code>.</p>
<h2 id="delegation-cloud-tasks-on-both-sides">Delegation: cloud tasks on both sides</h2>
<p>Both tools can now run work away from your laptop.</p>
<p><strong>Codex Cloud</strong> runs tasks remotely. You set up an environment in which Codex inspects your repositories, installs dependencies and tests the workflow. Then you start tasks from it. Each task has its own workspace and keeps running while your computer is asleep. You review the changed files, ask for follow-ups, and commit or open a pull request.</p>
<p><strong>Claude Code cloud sessions</strong> run on Anthropic-managed VMs. You can start them from claude.ai/code, the mobile app, the desktop app or the terminal with <code>claude --cloud &quot;&lt;task&gt;&quot;</code>. Each <code>--cloud</code> command starts its own independent session, so you can run several in parallel. <code>claude --teleport</code> pulls a cloud session back into your terminal. Cloud sessions are available on Pro, Max and Team plans, and for some Enterprise seats. For local parallel work, Claude Code also supports background agents.</p>
<p>Things to weigh:</p>
<ul>
<li><strong>Environment fidelity.</strong> Cloud tasks need your setup (dependencies, services, seed data) to be reproducible remotely. If your build only works on a developer&#39;s laptop, local sessions will be easier at first.</li>
<li><strong>Secrets and network.</strong> Both tools let you limit network access in cloud environments and add credentials for allowed services. Decide which credentials a remote environment may hold. Often the right answer is none.</li>
<li><strong>Review load.</strong> Parallel agents produce many diffs. The bottleneck moves from writing code to reviewing it.</li>
</ul>
<h2 id="code-review-and-pull-requests">Code review and pull requests</h2>
<p>Codex starts a review when someone comments <code>@codex review</code> on a pull request, and it can review pull requests automatically once automatic reviews are enabled. Claude Code&#39;s GitHub Action responds to <code>@claude</code> in issues and pull requests, and its separate Code Review product reviews every PR automatically. With Claude Code cloud sessions, auto-fix can also watch a PR and push fixes for CI failures and review comments.</p>
<p>Agent review works as a first pass. It catches missed edge cases, tests that don&#39;t match the code and obvious security mistakes. It doesn&#39;t replace an owner who understands the system. A sensible team rule is that an agent&#39;s review can block a merge, but only a human can approve one.</p>
<h2 id="where-each-one-fits">Where each one fits</h2>
<h3 id="choose-claude-code-if">Choose Claude Code if</h3>
<ul>
<li>You want detailed permission rules (allow, ask, deny) and one settings file the whole team commits.</li>
<li>You want hooks that run without a separate trust step, and subagents with their own tool limits.</li>
<li>You already pay for Claude plans, use the Anthropic API, or need a third-party provider in the CLI.</li>
</ul>
<h3 id="choose-codex-if">Choose Codex if</h3>
<ul>
<li>You prefer safety split into two settings: a hard sandbox boundary and an approval policy.</li>
<li>Your team already works in ChatGPT workspaces, where Codex is included from the Free plan up.</li>
<li>You want project config and hooks to load only once someone has marked the project as trusted.</li>
</ul>
<h3 id="using-both">Using both</h3>
<p>Some teams use one tool for interactive work and the other for delegated tasks, or have both review the same pull request. If you do this, keep one instruction file (Claude Code can read <code>AGENTS.md</code>) and one written permissions policy that you turn into each tool&#39;s config. Otherwise you end up maintaining two sets of rules that drift apart.</p>
<h2 id="the-workflow-problem-neither-tool-solves-alone">The workflow problem neither tool solves alone</h2>
<p>Both tools give you a capable agent and can run several at once. Neither one gives you a team process: who owns which task, what counts as &quot;done&quot;, when work may merge, and who approves a release. When teams run several agents, this missing layer is usually what breaks first. Two agents edit the same files, or a change is called finished without passing tests.</p>
<p>Some teams build that layer with scripts, worktrees and CI rules. Others use tools built on top of the agents. <a href="/atlas/">Atlas</a>, for example, runs on Claude Code. Each engineer role is its own Claude Code session with a written role and its own working folder. Atlas refuses to mark work complete until it is on main or queued for its landing train, which merges finished branches and runs the project&#39;s own checks on the result. Whatever you choose, define &quot;done&quot; before you increase the number of agents.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Claude Code and Codex now offer much the same set of features, so the choice comes down to workflow. Claude Code uses permission rules and modes, and its settings are shared through a committed file. Codex splits safety into a sandbox and an approval policy, and loads project config only from trusted projects. Run both on the same real tickets for a week, write down your permission defaults and instruction file, and measure reviewer time rather than how good the output looks at first glance.</p>
]]></content:encoded></item>
<item><title>Codex CLI Setup: Install, Approval Modes and Sandboxing</title><link>https://bridge-terminal.com/atlas/blog/codex-cli-install-approval-modes-sandbox/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/codex-cli-install-approval-modes-sandbox/</guid><pubDate>Wed, 07 Oct 2026 04:00:00 GMT</pubDate><description>Set up OpenAI Codex CLI: install it, sign in, write a config.toml and profile files, and choose the sandbox mode and approval policy for each kind of task.</description><category>codex</category><category>codex-cli</category><category>ai-agents</category><category>sandboxing</category><content:encoded><![CDATA[<ul><li>Codex CLI is OpenAI's open-source (Apache-2.0) coding agent for the terminal. Install it with the install script, npm or Homebrew, run codex in a project folder, and sign in with ChatGPT or an API key.</li><li>Two settings control what Codex can do: the sandbox mode (read-only, workspace-write or danger-full-access) limits what its commands can touch, and the approval policy (on-request or never) decides when it stops to ask you.</li><li>The current CLI no longer has --full-auto or the untrusted approval policy. Use --sandbox workspace-write with -a on-request, or --approve-for-me, which sends approval requests to automatic review.</li><li>Since Codex 0.134.0, profiles are separate files such as ~/.codex/review.config.toml, selected with --profile. [profiles.name] tables in config.toml are no longer read.</li><li>Use --dangerously-bypass-approvals-and-sandbox (alias --yolo) only in an environment that is already isolated, and commit before each session so you can review and revert changes.</li></ul><p>To set up OpenAI Codex CLI, install it, run <code>codex</code> in a project folder, and sign in with your ChatGPT account or an API key. Then decide how much Codex may do without asking you. Two settings control that. The <strong>sandbox mode</strong> limits what its commands can touch. The <strong>approval policy</strong> decides when it has to stop and ask. This guide covers both, plus a <code>config.toml</code> and profile files you can reuse.</p>
<p>Codex has changed a lot in recent releases. <code>--full-auto</code> and the <code>untrusted</code> approval policy are gone, and profiles have moved into separate files. Many older guides still show the earlier syntax, so this one points out where things changed.</p>
<h2 id="what-codex-cli-is">What Codex CLI is</h2>
<p>Codex CLI is OpenAI&#39;s coding agent for the terminal. It is open source under the Apache-2.0 licence, in the <code>openai/codex</code> repository on GitHub. It reads your repository, edits files and runs commands such as tests and builds. Because it acts on your machine, the sandbox and approval settings matter more than which model you pick.</p>
<h2 id="installing-codex-cli">Installing Codex CLI</h2>
<p>On macOS and Linux, the standalone installer is:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">curl -fsSL https://chatgpt.com/codex/install.sh | sh</code></pre></div>
<p>Running the same command again updates Codex. You can also install it with a package manager:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">npm install -g @openai/codex
# or, on macOS
brew install --cask codex</code></pre></div>
<p>On Windows, there is a PowerShell installer:</p>
<div class="code"><span class="lang">powershell</span><pre><code class="language-powershell">powershell -ExecutionPolicy ByPass -c &quot;irm https://chatgpt.com/codex/install.ps1 | iex&quot;</code></pre></div>
<p>The GitHub releases page also has prebuilt binaries for macOS (Apple silicon and Intel) and Linux (x86_64 and arm64). Each archive contains one file with the platform in its name, so you will usually rename it to <code>codex</code> after extracting it.</p>
<h2 id="signing-in">Signing in</h2>
<p>Start Codex from the root of a project:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">cd my-project
codex</code></pre></div>
<p>On first run, Codex asks how you want to authenticate:</p>
<ul>
<li><strong>Sign in with ChatGPT.</strong> Usage comes out of your ChatGPT plan, and you don&#39;t need to manage API keys.</li>
<li><strong>Use an API key.</strong> OpenAI bills this usage to your OpenAI Platform account at standard API rates. OpenAI recommends API keys for automation such as CI jobs.</li>
</ul>
<p>You can also sign in without starting a session. <code>codex login</code> opens a browser for the ChatGPT sign-in, and <code>--with-api-key</code> reads a key from standard input:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex login
printenv OPENAI_API_KEY | codex login --with-api-key</code></pre></div>
<h2 id="approval-policy-vs-sandbox-mode">Approval policy vs sandbox mode</h2>
<p>Setup often goes wrong because people treat these as one setting. There are two:</p>
<ul>
<li><strong>Sandbox mode</strong> is enforced by the operating system. It limits which files Codex&#39;s commands can write and whether they can reach the network. If the sandbox blocks something, it stays blocked, whatever the model decides.</li>
<li><strong>Approval policy</strong> decides when Codex stops for your OK, for example before it goes outside the sandbox.</li>
</ul>
<p>A tight sandbox with few prompts is often safer than a loose sandbox with many prompts. If you&#39;re asked to approve something every few seconds, you will soon start approving without reading.</p>
<h3 id="sandbox-modes">Sandbox modes</h3>
<p>Set the sandbox mode with <code>--sandbox</code> (or <code>-s</code>):</p>
<div class="table"><table><thead><tr><th>Mode</th><th>What it allows</th></tr></thead><tbody><tr><td><code>read-only</code></td><td>Codex can inspect files but can&#39;t edit them or run commands without approval</td></tr><tr><td><code>workspace-write</code></td><td>Read files, edit inside the workspace, run routine local commands there; network off by default</td></tr><tr><td><code>danger-full-access</code></td><td>No sandbox restrictions</td></tr></tbody></table></div>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex --sandbox read-only
codex --sandbox workspace-write</code></pre></div>
<p><code>workspace-write</code> is the default for local work. Codex can edit your project and run your test suite, but it can&#39;t write outside the workspace, and it has no network access unless you turn it on.</p>
<h3 id="how-the-sandbox-is-enforced">How the sandbox is enforced</h3>
<p>Codex uses operating-system features to enforce the sandbox, so it doesn&#39;t depend on the model following instructions:</p>
<ul>
<li><strong>macOS:</strong> Apple&#39;s built-in Seatbelt framework. It works without any setup.</li>
<li><strong>Linux and WSL2:</strong> bubblewrap (<code>bwrap</code>) together with seccomp. The docs say to install <code>bubblewrap</code> with your package manager first. Otherwise Codex falls back to a bundled helper, which needs unprivileged user namespaces.</li>
<li><strong>Windows:</strong> the native Windows sandbox when you run Codex in PowerShell. Under WSL2, Codex uses the Linux sandbox. WSL1 support ended with version 0.115.</li>
</ul>
<p>The limits apply to the commands Codex runs, including test scripts and install commands. If your environment can&#39;t provide these features, run Codex inside a container or VM that is already isolated, and loosen Codex&#39;s own sandbox only there.</p>
<h3 id="approval-policies">Approval policies</h3>
<p>Set the approval policy with <code>--ask-for-approval</code> (or <code>-a</code>). The CLI accepts two values:</p>
<ul>
<li><code>on-request</code>: Codex edits and runs commands inside the sandbox, and asks before it goes outside it, for example to use the network.</li>
<li><code>never</code>: Codex never asks. If the sandbox blocks something, the action fails.</li>
</ul>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex --sandbox workspace-write --ask-for-approval on-request</code></pre></div>
<p>In <code>config.toml</code> there is also a <code>granular</code> form. It keeps some kinds of approval prompt interactive and rejects the rest automatically.</p>
<p>Two older values no longer work. <code>untrusted</code> has been retired, and OpenAI says to remove it from your config. For cautious interactive use, it suggests <code>read-only</code> with <code>on-request</code>. <code>on-failure</code> is deprecated. The config reference says to use <code>on-request</code> for interactive runs and <code>never</code> for runs nobody is watching.</p>
<h3 id="shortcut-flags">Shortcut flags</h3>
<ul>
<li><strong><code>--sandbox workspace-write --ask-for-approval on-request</code></strong> is the &quot;Auto&quot; preset. Codex works inside the project and asks only when it needs to go further. This suits most day-to-day coding in a repository under version control.</li>
<li><strong><code>--approve-for-me</code></strong> (alias <code>--not-so-yolo</code>) uses the <code>workspace-write</code> sandbox with <code>on-request</code> approvals, but sends approval requests to an automatic review step instead of to you.</li>
<li><strong><code>--dangerously-bypass-approvals-and-sandbox</code></strong> (alias <code>--yolo</code>) runs every command without approvals and without a sandbox. The docs say to use it only inside an environment that is already hardened, such as a throwaway container or VM.</li>
</ul>
<p><code>--full-auto</code> is no longer supported. If an old script uses it, replace it with <code>--sandbox workspace-write</code> or <code>--approve-for-me</code>.</p>
<p>In a session, <code>/permissions</code> lets you change when Codex can edit files or run commands without asking, and shows the current sandbox and the folders it can write to. <code>/status</code> shows the session&#39;s current configuration.</p>
<h2 id="a-practical-config-toml">A practical config.toml</h2>
<p>Flags are fine for one-off runs. For settings you want every time, use <code>~/.codex/config.toml</code>:</p>
<div class="code"><span class="lang">toml</span><pre><code class="language-toml">approval_policy = &quot;on-request&quot;
sandbox_mode = &quot;workspace-write&quot;

[sandbox_workspace_write]
network_access = false</code></pre></div>
<p>Network access is already off by default. Setting it explicitly makes the choice visible to anyone reading the file.</p>
<p>This example doesn&#39;t set <code>model</code>, so Codex uses its default. To pin a model, add <code>model = &quot;...&quot;</code> with a model your account or workspace can use.</p>
<p>Codex also has newer <strong>permission profiles</strong>. You define them under <code>[permissions.&lt;name&gt;]</code> and select one with <code>default_permissions</code>. They can set rules per path, such as denying <code>.env</code> files, and limit network access to a list of allowed domains. They don&#39;t combine with <code>sandbox_mode</code> or <code>[sandbox_workspace_write]</code>, so choose one approach per config.</p>
<h3 id="profiles-for-different-levels-of-trust">Profiles for different levels of trust</h3>
<p>Profiles let you keep several setups and choose one for each run. Since Codex 0.134.0, each profile is its own file in <code>~/.codex</code>, named <code>&lt;profile&gt;.config.toml</code>. It uses top-level keys and contains only the values that differ from your base config. <code>--profile</code> no longer reads <code>[profiles.&lt;name&gt;]</code> tables in <code>config.toml</code>, so move any you have into separate files.</p>
<p><code>~/.codex/review.config.toml</code>:</p>
<div class="code"><span class="lang">toml</span><pre><code class="language-toml">approval_policy = &quot;on-request&quot;
sandbox_mode = &quot;read-only&quot;</code></pre></div>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex --profile review</code></pre></div>
<p>A read-only <code>review</code> profile works well when you&#39;re exploring an unfamiliar codebase or asking Codex to explain code. Run without the profile when you want it to make changes under the base config.</p>
<h3 id="one-off-overrides">One-off overrides</h3>
<p>You can override any config key for one run with <code>-c</code>:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex -c sandbox_workspace_write.network_access=true</code></pre></div>
<p>Use this when a task needs the network, such as installing a dependency, instead of turning network access on for every run.</p>
<h2 id="project-instructions-with-agents-md">Project instructions with AGENTS.md</h2>
<p>Codex reads <code>AGENTS.md</code> files before it starts any work. It reads a global one from <code>~/.codex</code>, then walks from the project root (usually the git root) down to your current directory, reading the <code>AGENTS.md</code> in each folder along the way. Running <code>/init</code> in a session creates one for you to edit.</p>
<p>Keep it short and concrete:</p>
<ul>
<li>The exact commands for install, lint, test and build</li>
<li>Directories that are generated or vendored and shouldn&#39;t be edited</li>
<li>Conventions a reviewer would otherwise flag</li>
</ul>
<h2 id="running-codex-without-the-interactive-ui">Running Codex without the interactive UI</h2>
<p>For scripts and CI, <code>codex exec</code> (alias <code>codex e</code>) runs a task and exits:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex exec &quot;explain why the test suite is failing&quot;</code></pre></div>
<p>By default, <code>codex exec</code> runs in a read-only sandbox, so Codex can&#39;t edit files. To let it make changes, grant write access explicitly:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">codex exec --sandbox workspace-write &quot;update the changelog for the latest commits&quot;</code></pre></div>
<p>In CI, set the API key only on the command that needs it, not for the whole job:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">CODEX_API_KEY=&lt;api-key&gt; codex exec --json &quot;triage open bug reports&quot;</code></pre></div>
<p>The docs warn against setting <code>OPENAI_API_KEY</code> or <code>CODEX_API_KEY</code> at job level when the job runs code from the repository, because build scripts or tests in that job could read the key. Keep the sandbox as narrow as the job allows. Nobody is reading prompts in a pipeline.</p>
<h2 id="choosing-settings-a-simple-ladder">Choosing settings: a simple ladder</h2>
<p>If you&#39;re not sure where to start, work up this list:</p>
<ol>
<li><strong>Exploring:</strong> <code>read-only</code> with <code>on-request</code>. Codex can read and explain code, but it can&#39;t change anything unless you approve it.</li>
<li><strong>Everyday coding:</strong> <code>workspace-write</code> with <code>on-request</code>, network off. Commit before each session so <code>git diff</code> shows exactly what changed.</li>
<li><strong>Tasks that need the network:</strong> the same setup, plus <code>-c sandbox_workspace_write.network_access=true</code> for that run only.</li>
<li><strong>Fully unattended:</strong> <code>--yolo</code> only inside a disposable container or VM, never against your laptop&#39;s home directory.</li>
</ol>
<p>Two habits matter more than the exact setting. Work on a clean git branch so you can review and revert any change. And read each command before you approve it, because approval prompts only protect you if someone reads them.</p>
<h2 id="beyond-a-single-agent">Beyond a single agent</h2>
<p>Approval policies govern one agent, one action at a time. When several agents work on the same product, you also need rules at the project level, such as who approves a production release and who can spend money or delete data. <a href="/atlas/">Atlas</a>, a team of AI engineers built on Claude Code, never makes those decisions itself: production go-lives, spending, deleting data, sending anything outside and dropping scope always go to a person.</p>
<h2 id="summary">Summary</h2>
<p>Installing Codex CLI takes a few minutes: install it, run <code>codex</code>, sign in. Most of the setup is choosing the sandbox (what Codex can touch) and the approval policy (when it asks), then saving both in <code>~/.codex/config.toml</code>. For most projects, start with <code>workspace-write</code>, <code>on-request</code> and network off. Add a read-only profile file for exploring, and use the full bypass only in a disposable environment. Flags have been renamed and removed recently, so check <code>codex --help</code> for your installed version before you put a flag in a script.</p>
]]></content:encoded></item>
<item><title>CLAUDE.md Best Practices: What to Put in Your Project File</title><link>https://bridge-terminal.com/atlas/blog/claude-md-best-practices-project-instructions/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-md-best-practices-project-instructions/</guid><pubDate>Tue, 06 Oct 2026 08:35:00 GMT</pubDate><description>What belongs in CLAUDE.md, what to cut, and when to use rules, imports, skills and hooks instead, based on Anthropic's Claude Code documentation.</description><category>claude-code</category><category>claude-md</category><category>ai-agents</category><category>developer-workflow</category><content:encoded><![CDATA[<ul><li>A good CLAUDE.md is short and specific. It holds only what Claude Code can't work out from your code: exact build and test commands, style rules that differ from defaults, and known gotchas.</li><li>Claude treats CLAUDE.md as context, not enforced configuration, so there is no guarantee it is always followed, especially when instructions are vague or conflict.</li><li>Aim for under 200 lines per CLAUDE.md file. Longer files use more context and lower adherence, and Claude Code warns at startup and in /status when a file is over the recommended length.</li><li>Anything that must happen every time belongs in a hook, which runs deterministically, not in CLAUDE.md, which is advisory.</li><li>Instructions that apply to only part of a project, or only matter sometimes, belong in path-scoped rules or skills. Imports organize a file but don't reduce its context cost.</li></ul><p>A good CLAUDE.md is short and specific. It holds what Claude Code can&#39;t work out from your code: the exact build and test commands, style rules that differ from the defaults, repository etiquette, and the gotchas people keep running into. Three kinds of content belong elsewhere: anything Claude can learn by reading the code, anything that applies to only one corner of the repo, and anything that must happen every time without exception.</p>
<p>This guide follows Anthropic&#39;s Claude Code documentation. It covers where the file goes, what to put in it, what to leave out, and what to check when Claude ignores it.</p>
<h2 id="what-claude-md-is-and-isnt">What CLAUDE.md is and isn&#39;t</h2>
<p>Every Claude Code session starts with a fresh context window. CLAUDE.md is a Markdown file you write, and Claude reads it at the start of every session, so you don&#39;t have to explain your project again each time.</p>
<p>Claude treats CLAUDE.md as <strong>context, not enforced configuration</strong>. According to the documentation, the content is &quot;delivered as a user message after the system prompt,&quot; and Claude tries to follow it, &quot;but there&#39;s no guarantee of strict compliance, especially for vague or conflicting instructions.&quot; So whether your conventions get followed depends on two things: how clearly you write them, and whether hard requirements live somewhere that enforces them.</p>
<h2 id="where-to-put-claude-md-files">Where to put CLAUDE.md files</h2>
<p>Claude Code reads instruction files from several places. They load from the broadest scope to the most specific:</p>
<div class="table"><table><thead><tr><th>Scope</th><th>Location</th><th>Use it for</th></tr></thead><tbody><tr><td>Managed policy</td><td><code>/Library/Application Support/ClaudeCode/CLAUDE.md</code> (macOS), <code>/etc/claude-code/CLAUDE.md</code> (Linux/WSL), <code>C:\Program Files\ClaudeCode\CLAUDE.md</code> (Windows)</td><td>Organization-wide instructions managed by IT or DevOps</td></tr><tr><td>User</td><td><code>~/.claude/CLAUDE.md</code></td><td>Your personal preferences across all projects</td></tr><tr><td>Project</td><td><code>./CLAUDE.md</code> or <code>./.claude/CLAUDE.md</code></td><td>Team conventions, checked into git</td></tr><tr><td>Local</td><td><code>./CLAUDE.local.md</code></td><td>Your private notes for this project; add it to <code>.gitignore</code></td></tr></tbody></table></div>
<p>Files are concatenated, not overridden. Claude Code loads CLAUDE.md and CLAUDE.local.md from your working directory and every directory above it. Files closer to where you launched Claude are read last, and within each directory <code>CLAUDE.local.md</code> comes after <code>CLAUDE.md</code>. A CLAUDE.md in a subdirectory below your working directory isn&#39;t loaded at launch. It loads once Claude reads, writes or edits a file in that subdirectory.</p>
<p>If you use git worktrees, note that a gitignored <code>CLAUDE.local.md</code> exists only in the worktree where you created it. To share personal instructions across worktrees, import a file from your home directory instead (imports are covered below).</p>
<h2 id="start-with-init-then-cut-it-down">Start with /init, then cut it down</h2>
<p>Run <code>/init</code> in a session to generate a first draft. Claude analyzes your codebase and writes a file with the build commands, test instructions and conventions it finds. If a CLAUDE.md already exists, <code>/init</code> suggests improvements instead of overwriting it. It also reads Cursor rules (<code>.cursor/rules/</code> or <code>.cursorrules</code>) and Copilot instructions (<code>.github/copilot-instructions.md</code>) and brings the relevant parts into the draft. For an interactive version, set <code>CLAUDE_CODE_NEW_INIT=1</code> before running <code>/init</code>. It asks which artifacts to set up (CLAUDE.md files, skills and hooks), asks follow-up questions, and shows you a proposal before it writes any files.</p>
<p>Treat the draft as a starting point. The documentation says to refine it &quot;with instructions Claude wouldn&#39;t discover on its own.&quot; Begin by cutting anything Claude could work out from the repository.</p>
<h2 id="what-to-put-in-claude-md">What to put in CLAUDE.md</h2>
<p>Anthropic&#39;s best-practices guide has an include/exclude table. The include side:</p>
<ul>
<li><strong>Bash commands Claude can&#39;t guess.</strong> Your real test, lint, typecheck and build commands, with the flags your project needs.</li>
<li><strong>Code style rules that differ from defaults.</strong> Skip anything the language&#39;s standard conventions already cover.</li>
<li><strong>Testing instructions and preferred test runners.</strong></li>
<li><strong>Repository etiquette.</strong> Branch naming and pull request conventions.</li>
<li><strong>Architectural decisions specific to your project.</strong></li>
<li><strong>Developer environment quirks</strong>, such as required environment variables.</li>
<li><strong>Common gotchas</strong>: behavior that isn&#39;t obvious from reading the code.</li>
</ul>
<p>The memory documentation also says when to add a line: Claude makes the same mistake a second time, a code review catches something Claude should have known, you type a correction you already typed last session, or a new teammate would need the same context to be productive.</p>
<h3 id="write-instructions-you-can-check">Write instructions you can check</h3>
<p>The docs recommend instructions &quot;concrete enough to verify,&quot; with these examples:</p>
<ul>
<li>&quot;Use 2-space indentation&quot; rather than &quot;Format code properly&quot;</li>
<li>&quot;Run <code>npm test</code> before committing&quot; rather than &quot;Test your changes&quot;</li>
<li>&quot;API handlers live in <code>src/api/handlers/</code>&quot; rather than &quot;Keep files organized&quot;</li>
</ul>
<p>Group related rules under Markdown headings and bullet points. No format is required. The example in Anthropic&#39;s guide is this short:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown"># Code style
- Use ES modules (import/export) syntax, not CommonJS (require)
- Destructure imports when possible (eg. import { foo } from 'bar')

# Workflow
- Be sure to typecheck when you're done making a series of code changes
- Prefer running single tests, and not the whole test suite, for performance</code></pre></div>
<h2 id="what-to-leave-out">What to leave out</h2>
<p>The exclude side of the table:</p>
<ul>
<li>Anything Claude can figure out by reading the code</li>
<li>Standard language conventions Claude already knows</li>
<li>Detailed API documentation (link to it instead)</li>
<li>Information that changes frequently</li>
<li>Long explanations or tutorials</li>
<li>File-by-file descriptions of the codebase</li>
<li>Self-evident practices like &quot;write clean code&quot;</li>
</ul>
<p>For each line, the guide suggests asking: <em>&quot;Would removing this cause Claude to make mistakes?&quot;</em> If not, cut it. The guide warns: &quot;Bloated CLAUDE.md files cause Claude to ignore your actual instructions!&quot; If Claude already does something correctly without an instruction, delete the instruction or turn it into a hook.</p>
<h3 id="keep-it-under-200-lines">Keep it under 200 lines</h3>
<p>The documented target is under 200 lines per CLAUDE.md file, because longer files consume more context and reduce adherence. Claude Code shows a warning at startup and in <code>/status</code> when one of your instruction files is over the recommended length. It also warns when several files that are each within that length add up past a combined limit. Each CLAUDE.md, rules file and <code>@path</code> import counts as a separate file. Files over 4 MiB are skipped entirely.</p>
<p>Two commands help with trimming. <code>/doctor</code> proposes cuts for a checked-in CLAUDE.md. It removes content Claude can derive from the codebase, such as directory layouts, dependency lists and architecture overviews, and keeps pitfalls, rationale and conventions that differ from tool defaults. <code>/doctor prompt-audit</code> (Claude Code v2.1.283 or later) checks your instruction files for problems: instructions written for older models, references to files or commands that don&#39;t exist, and files that contradict each other. It reports findings with proposed edits and changes nothing until you ask Claude to apply them.</p>
<h2 id="move-the-rest-into-rules-imports-skills-and-hooks">Move the rest into rules, imports, skills and hooks</h2>
<p>Content that isn&#39;t needed in every session can go somewhere else.</p>
<h3 id="path-scoped-rules-in-claude-rules">Path-scoped rules in .claude/rules/</h3>
<p>Put one topic per file, such as <code>testing.md</code> or <code>api-design.md</code>, in <code>.claude/rules/</code>. A rule without a <code>paths</code> field loads at launch with the same priority as <code>.claude/CLAUDE.md</code>. If you add <code>paths</code> frontmatter, the rule loads only when Claude works with matching files:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown">---
paths:
  - &quot;src/api/**/*.ts&quot;
---

# API Development Rules

- All API endpoints must include input validation
- Use the standard error response format
- Include OpenAPI documentation comments</code></pre></div>
<p>This works well for conventions that apply to only part of a monorepo. Personal rules can go in <code>~/.claude/rules/</code> and apply to every project on your machine. If a user rule and a project rule conflict, Claude may follow either, so keep them consistent.</p>
<h3 id="imports-with-path">Imports with @path</h3>
<p>CLAUDE.md can pull in other files with <code>@path/to/file</code>. Relative paths resolve relative to the file that contains the import, not the working directory. Imported files can import others, up to four hops deep:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">See @README for project overview and @package.json for available npm commands for this project.

# Additional Instructions
- git workflow @docs/git-instructions.md</code></pre></div>
<p>Imports help organize a long file, but they don&#39;t reduce its context cost, because imported files also load at launch. To mention a path without importing it, wrap it in backticks. The first time Claude Code finds imports that point outside your working directory in a project, it shows an approval dialog listing them.</p>
<h3 id="skills-for-procedures">Skills for procedures</h3>
<p>Multi-step procedures, and domain knowledge that only matters sometimes, belong in skills (<code>.claude/skills/&lt;name&gt;/SKILL.md</code>). A skill loads when you invoke it or when Claude decides it&#39;s relevant, so it doesn&#39;t take up context in every session.</p>
<h3 id="hooks-for-anything-that-must-always-happen">Hooks for anything that must always happen</h3>
<p>If a rule has to hold every time, such as running eslint after each file edit or blocking writes to the migrations folder, use a hook. In the best-practices guide&#39;s words, CLAUDE.md instructions are advisory, while hooks &quot;are deterministic and guarantee the action happens.&quot; Organizations have the same split. Managed settings such as <code>permissions.deny</code> are enforced by the client, while a managed CLAUDE.md shapes behavior but is &quot;not a hard enforcement layer.&quot;</p>
<h2 id="sharing-one-file-with-other-coding-agents">Sharing one file with other coding agents</h2>
<p>If your repository already has an <code>AGENTS.md</code> for other coding agents, Claude Code v2.1.277 or later reads it when there is no <code>CLAUDE.md</code>, <code>.claude/CLAUDE.md</code> or <code>CLAUDE.local.md</code> in your working directory or above it. If you want both files, the documented pattern is a CLAUDE.md that imports the shared file and then adds Claude-specific notes:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown">@AGENTS.md

## Claude Code

Use plan mode for changes under `src/billing/`.</code></pre></div>
<p>Watch out for one thing: a <code>CLAUDE.local.md</code> counts as having a CLAUDE.md, so adding one stops Claude from reading <code>AGENTS.md</code> by default. To keep both, open <code>/config</code> and set <strong>Project instructions</strong> to <code>claude-md-and-agents-md</code>.</p>
<h2 id="when-claude-ignores-your-claude-md">When Claude ignores your CLAUDE.md</h2>
<p>Work through these checks in order:</p>
<ol>
<li><strong>Confirm it loaded.</strong> Run <code>/context</code> and look under <strong>Memory files</strong>. If the file isn&#39;t listed, Claude can&#39;t see it. Subdirectory files don&#39;t appear there because they load on demand.</li>
<li><strong>Look for conflicts.</strong> If two files give different guidance for the same behavior, Claude may pick one arbitrarily.</li>
<li><strong>Make the rule more specific.</strong> If Claude asks questions that CLAUDE.md already answers, the wording may be ambiguous.</li>
<li><strong>Shorten the file.</strong> If Claude keeps doing something despite a rule against it, the guide says the file &quot;is probably too long and the rule is getting lost.&quot;</li>
<li><strong>Emphasize sparingly.</strong> If Claude keeps skipping one instruction, adding &quot;IMPORTANT&quot; to that line alone can help. Emphasize many lines and none stands out.</li>
<li><strong>Check for built-in guidance.</strong> If your file sets commit or pull request rules, turn off Claude Code&#39;s own git instructions with the <code>includeGitInstructions</code> setting.</li>
</ol>
<p>If an instruction disappears after <code>/compact</code>, it was probably given only in chat, or it lives in a nested CLAUDE.md or path-scoped rule that hasn&#39;t loaded again yet. Claude re-reads the project-root CLAUDE.md from disk after compaction, so moving a chat instruction into that file keeps it.</p>
<p>Two smaller details: block-level HTML comments (<code>&lt;!-- notes --&gt;</code>) are stripped before the content reaches Claude, so you can leave notes for human maintainers without spending context. And asking Claude to &quot;remember&quot; something saves it to auto memory, not CLAUDE.md. To put it in the shared file, say &quot;add this to CLAUDE.md&quot; or edit the file yourself through <code>/memory</code>.</p>
<h2 id="when-you-run-several-agents">When you run several agents</h2>
<p>The same principles apply when several Claude Code sessions work on one codebase. Each session needs its role and constraints written down, and anything that must always hold needs a mechanism that enforces it, not just a sentence. <a href="/atlas/">Atlas</a> follows this pattern. Each engineer is its own Claude Code session with a written role and its own working folder. &quot;Done&quot; is enforced: completion is refused until the work is on main or queued for the landing train.</p>
<h2 id="summary">Summary</h2>
<p>The guide&#39;s advice is to treat CLAUDE.md like code. Generate a draft with <code>/init</code>, cut everything Claude could work out from the repository, and keep the rest concrete and under 200 lines. Move conventions that apply to only part of the codebase into path-scoped rules, procedures into skills, and steps that must always happen into hooks. Check the file into git, review it when Claude gets something wrong, and use <code>/context</code> to confirm what loaded.</p>
]]></content:encoded></item>
<item><title>Claude Code Worktrees: Run Parallel Sessions Safely</title><link>https://bridge-terminal.com/atlas/blog/claude-code-git-worktrees-parallel-sessions/</link><guid isPermaLink="true">https://bridge-terminal.com/atlas/blog/claude-code-git-worktrees-parallel-sessions/</guid><pubDate>Mon, 05 Oct 2026 03:40:00 GMT</pubDate><description>How to run parallel Claude Code sessions with git worktrees: the --worktree flag, .worktreeinclude, subagent isolation, cleanup rules and merging back.</description><category>claude-code</category><category>git-worktrees</category><category>ai-agents</category><category>parallel-agents</category><content:encoded><![CDATA[<ul><li>To stop several Claude Code sessions from overwriting each other's edits, give each session its own git worktree: a separate working folder linked to the same repository.</li><li>Running claude --worktree &lt;name&gt; creates .claude/worktrees/&lt;name&gt;/ on a new branch called worktree-&lt;name&gt; and starts the session there. The repository needs at least one commit first.</li><li>A new worktree has no installed dependencies or .env files. List gitignored config files in a .worktreeinclude file and Claude Code copies them into each new worktree.</li><li>Claude Code blocks some edits and git commands that reach back into the main checkout, but the docs say this is not a security boundary. Worktrees also don't separate ports, databases or the final merge.</li><li>Worktrees from non-interactive -p runs are not cleaned up automatically. Remove them with git worktree remove, running git worktree unlock first if git says the worktree is locked.</li></ul><p>To run several Claude Code sessions on one repository without them overwriting each other&#39;s edits, give each session its own git worktree. Claude Code does this with one flag, <code>claude --worktree &lt;name&gt;</code>. It creates a separate checkout on a new branch and starts the session inside it. This guide covers setup, the files a new worktree is missing, subagent isolation, cleanup, and the parts you still have to handle yourself.</p>
<h2 id="why-parallel-agents-need-git-worktrees">Why parallel agents need git worktrees</h2>
<p>Two agent sessions in one working directory share one set of files. One session&#39;s refactor can land in the middle of the other&#39;s test run, and both write to the same index. Separate branches don&#39;t solve this, because a single checkout can only have one branch checked out at a time.</p>
<p>A <a href="https://git-scm.com/docs/git-worktree" rel="noopener">git worktree</a> is an extra working directory linked to the same repository. Per the git documentation, each worktree gets its own <code>HEAD</code> and index. Refs under <code>refs/</code>, branches and tags included, are shared, apart from a few per-worktree ones such as <code>refs/bisect</code>. The repository&#39;s <code>config</code> file is also shared by default. What that means for you:</p>
<ul>
<li>A branch created or updated in one worktree is visible from all the others immediately, with no push or fetch.</li>
<li>Each worktree has its own files on disk, so edits in one don&#39;t touch the others.</li>
<li>By default, <code>git worktree add</code> refuses to check out a branch that&#39;s already checked out in another worktree. <code>--force</code> overrides this.</li>
</ul>
<p>That last rule keeps agents apart: each session ends up on its own branch.</p>
<h2 id="start-claude-code-in-a-worktree">Start Claude Code in a worktree</h2>
<p>Pass <code>--worktree</code> (or <code>-w</code>) with a name:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude --worktree feature-auth</code></pre></div>
<p>According to the Claude Code documentation, this creates the worktree under <code>.claude/worktrees/feature-auth/</code> at the repository root, on a new branch named <code>worktree-feature-auth</code>, and starts the session there. Run the same command in a second terminal with a different name to get a second isolated session. If you leave out the name, Claude generates one, such as <code>bright-running-fox</code>. Pass a name whose directory already exists and Claude Code reopens that worktree rather than creating a new one.</p>
<p>There are two prerequisites:</p>
<ul>
<li><strong>At least one commit.</strong> A worktree is created from an existing commit. In a repository with no commits, the command fails with <code>Failed to resolve base branch &quot;HEAD&quot;: git rev-parse failed</code>.</li>
<li><strong>Workspace trust.</strong> Interactive runs need it. If you haven&#39;t used Claude in that directory before, run <code>claude</code> there once and accept the trust dialog. Non-interactive runs with <code>-p</code> skip this check.</li>
</ul>
<p>Add the worktree folder to <code>.gitignore</code> so its contents don&#39;t show up as untracked files in your main checkout:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">.claude/worktrees/</code></pre></div>
<p>You can also ask Claude to &quot;work in a worktree&quot; during a session, and it creates one with its <code>EnterWorktree</code> tool. If Claude tries to enter a path outside the repository&#39;s <code>.claude/worktrees/</code> directory, Claude Code asks for your approval first. Neither a permission rule nor &quot;don&#39;t ask again&quot; turns this prompt off. Only <code>bypassPermissions</code> mode skips it. The prompt arrived in v2.1.206. Before that, Claude could enter any existing worktree without asking. Several other behaviors in this guide also depend on recent 2.1.x versions, so on an older version some of them will work differently.</p>
<h3 id="choose-what-the-worktree-branches-from">Choose what the worktree branches from</h3>
<p>By default, a new worktree branches from the repository&#39;s default branch on the remote (usually <code>main</code>), so it starts from a clean tree. To branch from your current local <code>HEAD</code> instead, bringing along unpushed commits and feature-branch state, set <code>worktree.baseRef</code> in settings:</p>
<div class="code"><span class="lang">json</span><pre><code class="language-json">{
  &quot;worktree&quot;: {
    &quot;baseRef&quot;: &quot;head&quot;
  }
}</code></pre></div>
<p>The setting has two values: <code>&quot;fresh&quot;</code> (the default) and <code>&quot;head&quot;</code>. Inside a worktree, <code>&quot;head&quot;</code> means that worktree&#39;s <code>HEAD</code>, not the main checkout&#39;s. You can&#39;t set it to a branch name. To start from a specific existing branch, create the worktree with git yourself (see below).</p>
<p>To review or continue a pull request in isolation, pass its number with a <code>#</code> prefix. Quote it so your shell doesn&#39;t treat <code>#</code> as the start of a comment:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">claude --worktree &quot;#1234&quot;</code></pre></div>
<p>Claude Code fetches that pull request&#39;s head commit from <code>origin</code> and creates the worktree at <code>.claude/worktrees/pr-1234</code>. You can also pass a GitHub pull request URL or a GitLab merge request URL. Claude Code reads only the number from the URL and always fetches from <code>origin</code>.</p>
<h2 id="set-up-the-worktree-environment">Set up the worktree environment</h2>
<p>A worktree is a fresh checkout of tracked files. It has none of your gitignored local files, so expect no <code>node_modules</code>, virtualenv, build cache or <code>.env</code>. Set up each worktree, either by asking Claude to install dependencies or by running your project&#39;s setup in the worktree directory yourself.</p>
<p>For gitignored files such as environment configs, add a <code>.worktreeinclude</code> file at the project root. It uses <code>.gitignore</code> syntax. Claude Code copies files that match a pattern <strong>and</strong> are gitignored into each new worktree, so tracked files are never duplicated. This example comes from the documentation:</p>
<div class="code"><span class="lang">text</span><pre><code class="language-text">.env
.env.local
config/secrets.json</code></pre></div>
<p>This applies to every worktree Claude Code creates with git: <code>--worktree</code> sessions, subagent worktrees and parallel sessions in the desktop app. It doesn&#39;t apply if you replaced worktree creation with a <code>WorktreeCreate</code> hook. In that case, copy the files in your hook script.</p>
<p>Two things to plan for when several worktrees run at once:</p>
<ul>
<li><strong>Shared local resources.</strong> The Claude Code docs don&#39;t cover this, but separate file trees don&#39;t give you separate ports, databases or Docker containers. If every worktree starts a dev server on the same port or migrates the same local database, the sessions will collide. Give each worktree its own port or database name, for example through its env file.</li>
<li><strong>Git LFS.</strong> If you set up LFS with <code>git lfs install --local</code>, worktrees that Claude Code creates contain LFS pointer files, not the real files. Claude Code skips filter drivers defined in the repository&#39;s own config when it creates a worktree, because a filter driver is a shell command that anything able to write to the repository could have planted. A plain <code>git lfs install</code>, which writes to your global config, isn&#39;t affected. To get the real files, run <code>git lfs pull</code> inside the worktree.</li>
</ul>
<h2 id="isolate-subagents-with-worktrees">Isolate subagents with worktrees</h2>
<p>Subagents that Claude spawns can each get their own worktree too, so their parallel edits don&#39;t conflict. Ask Claude to &quot;use worktrees for your agents,&quot; or make it permanent for a custom subagent by adding <code>isolation: worktree</code> to its frontmatter:</p>
<div class="code"><span class="lang">markdown</span><pre><code class="language-markdown">---
name: refactorer
description: Applies mechanical refactors across many files
isolation: worktree
---

Apply the requested refactor across every affected file, then run the tests
and report the results.</code></pre></div>
<p>Put that file in <code>.claude/agents/</code>. Each run gets a temporary worktree, and Claude Code removes it automatically if the subagent finishes without changes. A worktree with changes stays on disk. A periodic sweep removes subagent and background-session worktrees once they are older than your <code>cleanupPeriodDays</code> setting, but it leaves any worktree that still has changed or untracked files or unpushed commits. Subagent worktrees use the same base-branch setting as <code>--worktree</code>. If subagents need to build on unpushed work, set <code>worktree.baseRef</code> to <code>&quot;head&quot;</code>.</p>
<h2 id="what-claude-code-enforces-inside-a-worktree">What Claude Code enforces inside a worktree</h2>
<p>While a session is isolated in a worktree, Claude Code blocks some tool calls that would reach back into the main checkout. The same checks apply to subagents spawned from that session. The documentation lists four:</p>
<ol>
<li><strong>File edits</strong>: blocks an <code>Edit</code>, <code>Write</code> or <code>NotebookEdit</code> call that targets a path in the main checkout.</li>
<li><strong>Command working directory</strong>: blocks a command whose working directory resolves to the main checkout, or whose working directory it can&#39;t confirm stays outside it.</li>
<li><strong>Git redirects</strong>: blocks a command that points git at the main checkout through <code>git -C</code>, <code>--git-dir</code>, a <code>GIT_DIR</code> or <code>GIT_WORK_TREE</code> variable, or a <code>cd</code> into the main checkout before running git.</li>
<li><strong>Command shape</strong>: blocks a command when Claude Code can&#39;t tell from its text that any git it runs stays inside the worktree. You can&#39;t turn this check off.</li>
</ol>
<p>PowerShell commands get only the working-directory check. The documentation also spells out the limits: none of the checks tracks which files a shell command writes, so a <code>cp</code> or shell redirect into the main checkout isn&#39;t caught. Those commands fall under your normal permission and sandbox settings. Treat worktree isolation as protection against accidents, not as a security boundary.</p>
<p>Some things are shared on purpose. If you choose &quot;Yes, and don&#39;t ask again&quot; for a Bash command in a worktree session, the rule goes into the main checkout&#39;s <code>.claude/settings.local.json</code>. It then applies in the main checkout and in every other worktree of the repository, and it stays after the worktree is removed. On Windows, and in a few other cases the docs list, the rule stays with the worktree instead. Plugins installed at project scope from the main checkout also load in its worktrees.</p>
<h2 id="clean-up-worktrees">Clean up worktrees</h2>
<p>When you exit an interactive worktree session, Claude Code checks for anything removal would destroy: changed or untracked files, uncommitted work in checked-out submodules, and new commits.</p>
<ul>
<li><strong>Clean worktree</strong>: for an unnamed session, Claude removes the worktree and its branch automatically. A named session asks first.</li>
<li><strong>Worktree with work in it</strong>: you choose whether to keep it or remove it. If you keep it, Claude Code prints a <code>claude --worktree &lt;name&gt; --resume</code> command so you can come back later. Removing it deletes the directory, the branch and all the work in them.</li>
<li><strong>State can&#39;t be verified</strong>: Claude Code asks you rather than removing it automatically.</li>
</ul>
<p>Non-interactive runs (<code>-p</code>) have no exit prompt, so Claude doesn&#39;t clean up their worktrees. The lock Claude Code put on each one at creation also stays until a later session&#39;s sweep releases it. Remove these worktrees with git, unlocking first if git refuses because the worktree is locked:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">git worktree list
git worktree unlock .claude/worktrees/feature-auth
git worktree remove .claude/worktrees/feature-auth</code></pre></div>
<p><code>git worktree remove</code> only removes clean worktrees, meaning no untracked files and no changes to tracked files. A worktree that isn&#39;t clean, or that contains submodules, needs <code>--force</code>, so check first that nothing in it matters. If you deleted a worktree directory by hand, <code>git worktree prune</code> removes the leftover administrative data. Add <code>-n</code> to see what it would remove without removing anything.</p>
<h2 id="manage-worktrees-manually">Manage worktrees manually</h2>
<p>Use git directly when you need an existing branch or a location outside the repository:</p>
<div class="code"><span class="lang">bash</span><pre><code class="language-bash">git worktree add ../project-feature-a -b feature-a
git worktree add ../project-bugfix fix-issue-456
cd ../project-feature-a
claude</code></pre></div>
<p>The first command creates a new branch. The second checks out a branch that already exists. Claude Code&#39;s sweep never removes worktrees you created yourself with <code>git worktree add</code>, so cleaning those up is your job.</p>
<h2 id="merging-parallel-work-back">Merging parallel work back</h2>
<p>Worktrees keep work apart, but they don&#39;t put it back together. Five sessions on five branches means five merges, and any conflicts the worktrees postponed show up then. Some habits that help:</p>
<ul>
<li><strong>Split work by area, not by size.</strong> Two agents editing the same module will conflict however well their files are isolated.</li>
<li><strong>Merge often.</strong> Short-lived branches drift less from <code>main</code>.</li>
<li><strong>Run your full checks on the merged result,</strong> not only on each branch. Two branches can each pass alone and still break when combined.</li>
</ul>
<p>Atlas, an AI engineering team built on Claude Code, handles this step for its own engineers. Each engineer works in its own copy of the code, so engineers never edit the same files under each other. Finished work goes to a landing train, which merges up to six finished branches, runs the project&#39;s own checks once on the combined result, and splits a failing batch until it finds the branch that broke it. Main&#39;s history is never rewritten. Each test run also gets its own copy of the project&#39;s database per worker, which covers part of the shared-database problem described above.</p>
<h2 id="checklist">Checklist</h2>
<ol>
<li>Make at least one commit and add <code>.claude/worktrees/</code> to <code>.gitignore</code>.</li>
<li>Write a <code>.worktreeinclude</code> for your env files.</li>
<li>Start each session with <code>claude --worktree &lt;name&gt;</code>.</li>
<li>Add <code>isolation: worktree</code> to subagents that edit files in parallel.</li>
<li>Give each worktree its own ports and databases.</li>
<li>Remove worktrees left by <code>-p</code> runs yourself.</li>
</ol>
<p>For more on running a team of AI engineers, see the <a href="/atlas/">Atlas home page</a>.</p>
]]></content:encoded></item>
</channel></rss>
