●SUNSET — The legacy Workbench retires today, August 17: saved prompts, prompt versions, and evals become inaccessible after that, so exporting is a today-or-never job●API — The experimental prompt tool endpoints generate_prompt, improve_prompt, and templatize_prompt retire the same day and will return errors, so any script calling them needs switching over today●CONSOLE — The replacement Workbench is stateless: nothing is stored on Anthropic's servers, your draft stays in the browser, and any request can be exported as code●OSS — Claude for Open Source now grants six months of Claude Max 20x to qualifying maintainers, capped at 10,000 people, individual only, with no API credits and no auto-renewal●QUOTA — The 50 percent weekly usage boost for Claude Code subscribers runs through August 19, two days out●SELF-HOSTED — Self-hosted environments for Claude Code are in public beta, letting Team and Enterprise plans run sessions on their own infrastructure with internal network access and custom tooling●SUNSET — The legacy Workbench retires today, August 17: saved prompts, prompt versions, and evals become inaccessible after that, so exporting is a today-or-never job●API — The experimental prompt tool endpoints generate_prompt, improve_prompt, and templatize_prompt retire the same day and will return errors, so any script calling them needs switching over today●CONSOLE — The replacement Workbench is stateless: nothing is stored on Anthropic's servers, your draft stays in the browser, and any request can be exported as code●OSS — Claude for Open Source now grants six months of Claude Max 20x to qualifying maintainers, capped at 10,000 people, individual only, with no API credits and no auto-renewal●QUOTA — The 50 percent weekly usage boost for Claude Code subscribers runs through August 19, two days out●SELF-HOSTED — Self-hosted environments for Claude Code are in public beta, letting Team and Enterprise plans run sessions on their own infrastructure with internal network access and custom tooling
Now that forking is the default, review agents are the ones I still call by name
Subagent forking became the default, so delegated work now inherits the parent conversation. Work can inherit context. Judgment cannot. Here is how I declare that boundary in the repository and catch it drifting.
I was splitting up my pre-release checks when the review agent's replies got noticeably shorter.
The same agent that used to push back with "this claim has no supporting evidence" came back with "as already confirmed above, no issues found." It took me a while to realize the pushback had not weakened. The agent was no longer in a position to push back at all.
The cause was not my prompt. The default for delegation had changed underneath me.
The short version: work can inherit context, judgment cannot
Handing a subagent the parent conversation saves you from re-explaining everything. It also saves the agent from re-reading files the parent already read. For pure execution work, that is a straightforward win.
But review, re-verification, and audit work all ask the same question: is the conclusion the parent reached actually correct? The moment that agent inherits the parent conversation, it stops being a third party. It becomes a participant who arrived at the conclusion alongside you.
So the line gets drawn by role, not by capability.
What you delegate
Parent context
Why
Enumeration and collection (asset inventory, string diffing)
Inherit
Independence of the conclusion is irrelevant. Re-explaining the premise is pure overhead
Procedural execution (build settings, version consistency)
Inherit
Little judgment involved, and more context means fewer round trips
Review and critique
Do not inherit
Knowing the parent's reasoning means only ever reading from inside that reasoning
Pass or fail re-verification
Do not inherit
"It was reported as passing" hands over the answer before the check begins
What changed on August 14 was where unnamed delegation lands
In Claude Code 2.1.232, subagent forking became enabled by default. A subagent with subagent_type: "fork" inherits the parent session's full conversation and its prompt cache. In interactive sessions, non-teammate agent spawns now run in the background by default.
The part that is easy to miss is when forking actually fires. It fires when you delegate without specifying subagent_type. A call that names a subagent explicitly still starts from an empty context. As the subagents documentation puts it, a named subagent does not see your conversation history or the files Claude already read.
So the intuition "the default changed, therefore all my agents now inherit" is backwards. What changed was the behavior of delegation where you never named anyone — the "just take a quick look at this" kind.
That is exactly how my review agent ended up rubber-stamping. The definition file was sitting right there in .claude/agents/. My actual calls were skipping it. Writing the definition had felt like finishing the job, and I never checked whether the call site was routing through it.
You can declare the direction explicitly:
Setting
Effect
CLAUDE_CODE_FORK_SUBAGENT=1
Enables forking in non-interactive runs and the Agent SDK as well
CLAUDE_CODE_FORK_SUBAGENT=0
Disables forking in every kind of session
Deny Agent(fork)
Keeps forking available, but stops unnamed delegation from falling into it
The third row is the one I reach for most.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You will be able to stop a review or verification agent from quietly turning into a rubber stamp, before it costs you a bad release
✦You will be able to sort your own agent definitions into work that should inherit context and judgment that must not
✦You will be able to tell which setups actually get cheaper from forking and which barely do, so you know whether turning the default off is worth it
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Inherited context reduces findings because the agent is behaving correctly
Rereading those rubber-stamped replies, something clicked. The review agent was not being lazy. Given the context it received, it was behaving exactly as it should.
The parent conversation contains my reasoning for committing to an approach, plus every piece of evidence I lined up behind it. To an agent handed that history, the conclusion is settled prior work. Raising "this judgment may be wrong" would mean doubting the very context it was told to trust — which is in direct conflict with the point of inheriting context in the first place.
Which is why independence cannot be restored through instructions. You can add "please question the premises," but as long as the premises arrive as conversation history, the starting point does not move.
The only way to restore independence is to not hand it over.
Four questions I use to decide
Every time I add an agent, I run through these in order.
Could this agent's output contradict the parent's conclusion? If yes, no inheritance. Review, re-verification, and final ship/no-ship checks all land here.
How many round trips does re-explaining the premise actually cost? If it is one or two, inheritance buys almost nothing. Just call the agent by name.
How many run in parallel? Once you have two or more children, sharing the cached prefix starts to matter. With a single delegation, that benefit barely shows up.
Is everything in the parent conversation safe to show this agent? Conversations accumulate dead-end experiments and findings from unrelated work. A narrow job has no reason to receive a wide context.
Question three deserves a note, because it is the one people get wrong in the cheap direction. Forking lowers input cost by letting children share the parent's prompt prefix, and the effect kicks in from the second child onward. If you fire delegations one at a time in sequence, the savings stay small. "Forking is cheaper, so always fork" does not hold for every shape of work.
Declare the boundary in the repository, not in someone's shell profile
A boundary that lives only in your head will drift. Mine drifted until a rubber-stamped review made it visible.
That pairing looks contradictory at first glance. The intent:
Set CLAUDE_CODE_FORK_SUBAGENT to 1 so forking is available even in non-interactive runs, because I want the cost savings on parallel fan-out
Deny Agent(fork) to close only the path where an unnamed delegation silently becomes a fork
With that in place, inheriting context becomes something you have to choose on purpose. The accidental route disappears and the deliberate one remains.
If you never fork at all, setting CLAUDE_CODE_FORK_SUBAGENT to 0 is simpler — but you give up the parallel cost benefit along with it. I wanted that benefit for parallel asset scans, so I landed on the configuration above.
The pitfall I hit right after flipping the default
The day after I denied Agent(fork), work stopped a few times.
What stopped were delegations with no named definition behind them. I still had the habit of tossing small lookups over as "just go check this," and instead of falling into a fork, that path now got rejected outright. The deny rule protects independence, but it also locks out every delegation that has no landing spot.
There are two ways to handle it:
Add one general-purpose named agent as a landing spot and route the previously unnamed work through it
Temporarily allow it again and tidy up the definitions afterward
I recommend the first. Creating a landing spot means the work you were throwing around unnamed ends up written down as a definition file, which becomes the raw material for the next time you redraw the line. The second option keeps you moving today, but nothing survives to explain why things stopped.
There is also a workaround that makes the migration itself easier: before adding the deny rule, work normally for a day and count how many unnamed delegations you actually issue. If the count is high, build the landing spot first. If it is low, just deny and move on. Mine was higher than I expected, so I built the landing spot first.
I only wired this into the production pre-submission procedure after knowing that number. When a delegation gets rejected mid-check, it is hard to tell whether the check failed or the configuration refused, and separating those two costs more time than it should.
Let a script find the drift between definitions and posture
Declaring the posture once is not enough. Add a few more agents and the line moves again. So I wrote a small auditor that reads the definition files and sorts them, using role words (review, re-verify, audit, third party) to pick out the ones that must not inherit.
#!/usr/bin/env python3"""fork_scope_audit.py — sort agents by whether they may inherit parent context.Usage: python3 fork_scope_audit.py [PROJECT_ROOT]"""from __future__ import annotationsimport jsonimport reimport sysfrom dataclasses import dataclassfrom pathlib import Path# Words that signal a job requiring independence. Checked in description *and* body.ISOLATE_MARKERS = ( "review", "verify", "audit", "critique", "double-check", "second opinion", "independently", "third party", "overlooked",)# Phrasing that assumes inherited context (instructions that need the parent)INHERIT_HINTS = ("as above", "earlier", "this conversation", "follow up", "continue")FM = re.compile(r"^---\s*\n(.*?)\n---\s*\n(.*)$", re.S)@dataclassclass Agent: path: Path name: str description: str body: str @property def haystack(self) -> str: return f"{self.name}\n{self.description}\n{self.body}".lower() def isolate_reasons(self) -> list[str]: return [m for m in ISOLATE_MARKERS if m in self.haystack] def inherit_reasons(self) -> list[str]: return [m for m in INHERIT_HINTS if m in self.haystack]def parse_agent(path: Path) -> Agent | None: m = FM.match(path.read_text(encoding="utf-8")) if not m: return None front, body = m.group(1), m.group(2) fields: dict[str, str] = {} for line in front.splitlines(): # top-level keys only; nested values are not agent metadata if ":" in line and not line.startswith(" "): k, _, v = line.partition(":") fields[k.strip()] = v.strip().strip('"').strip("'") return Agent(path, fields.get("name", path.stem), fields.get("description", ""), body)def read_posture(root: Path) -> dict[str, object]: """Read the current forking posture out of settings.json.""" posture: dict[str, object] = {"env": None, "deny_fork": False} # read in this order so local settings win for rel in (".claude/settings.json", ".claude/settings.local.json"): p = root / rel if not p.exists(): continue data = json.loads(p.read_text(encoding="utf-8") or "{}") env = (data.get("env") or {}).get("CLAUDE_CODE_FORK_SUBAGENT") if env is not None: posture["env"] = str(env) deny = (data.get("permissions") or {}).get("deny") or [] if any(str(r).replace(" ", "").lower() == "agent(fork)" for r in deny): posture["deny_fork"] = True return posturedef main(argv: list[str]) -> int: root = Path(argv[1] if len(argv) > 1 else ".").resolve() agent_dir = root / ".claude" / "agents" if not agent_dir.is_dir(): print(f"No agent definitions found at {agent_dir}") return 2 posture = read_posture(root) agents = [a for a in (parse_agent(p) for p in sorted(agent_dir.glob("*.md"))) if a] isolate = [(a, r) for a in agents if (r := a.isolate_reasons())] inherit = [a for a in agents if not a.isolate_reasons()] env = posture["env"] effective = "off" if env == "0" else "on" declared = "declared" if env is not None else "undeclared (falls back to the default)" print(f"Target: {root}") print(f"Agent definitions: {len(agents)}") print(f"CLAUDE_CODE_FORK_SUBAGENT: {declared} -> effective fork={effective}") print(f"Agent(fork) deny rule: {'present' if posture['deny_fork'] else 'absent'}") print() print(f"[must not inherit] {len(isolate)} - call by name, withhold parent context") for a, reasons in isolate: print(f" - {a.name:<20} matched: {', '.join(reasons[:3])}") print() print(f"[may inherit] {len(inherit)} - forking is fine") for a in inherit: hints = a.inherit_reasons() tail = f" (assumes inherited context: {', '.join(hints[:2])})" if hints else "" print(f" - {a.name}{tail}") print() problems = 0 if isolate and effective == "on" and not posture["deny_fork"]: problems += 1 print("WARN: agents that need independence exist, but unnamed delegation still forks.") print(" Fix: always call them by name, or deny Agent(fork).") if env is None: problems += 1 print("WARN: the forking posture is not declared in the repository.") print(" Fix: set CLAUDE_CODE_FORK_SUBAGENT under env in .claude/settings.json.") if not problems: print("OK: the inheritance boundary matches what is declared.") return 1 if problems else 0if __name__ == "__main__": raise SystemExit(main(sys.argv))
Scanning the body and not just the description is deliberate. Names and descriptions stay short, and "reviews this independently" is usually a property that only shows up in the system prompt below the frontmatter.
Run against a project before the posture is declared:
Target: /path/to/projectAgent definitions: 5CLAUDE_CODE_FORK_SUBAGENT: undeclared (falls back to the default) -> effective fork=onAgent(fork) deny rule: absent[must not inherit] 2 - call by name, withhold parent context - change-review matched: review, third party, independently - gate-verify matched: verify, double-check[may inherit] 3 - forking is fine - asset-inventory - release-preflight - store-copy-i18nWARN: agents that need independence exist, but unnamed delegation still forks. Fix: always call them by name, or deny Agent(fork).WARN: the forking posture is not declared in the repository. Fix: set CLAUDE_CODE_FORK_SUBAGENT under env in .claude/settings.json.
Drop in the settings.json from earlier, run it again, and the warnings clear with an exit code of 0:
CLAUDE_CODE_FORK_SUBAGENT: declared -> effective fork=onAgent(fork) deny rule: present...OK: the inheritance boundary matches what is declared.
Because it returns an exit code, it drops straight into a pre-release check. An inheritance boundary is the kind of agreement that is too fine-grained to hold by eye.
How this landed in a solo, multi-app workflow
I run several wallpaper apps on my own, and I split the pre-submission checks across a few agents: asset inventory, diffing Japanese and English store copy, and verifying version numbers and signing settings. All of those finish faster with the parent context available, so all of them inherit.
The one remaining agent — the final "is this safe to submit" check — gets called by name. It receives the target and the pass criteria, and nothing of the conversation that produced them.
Since the split, that final check has been returning longer answers. Most of the extra length is it re-asking about things I had already decided were fixed. Reading it is mildly annoying, which I have come to think is the point. A check that never inconveniences you is not checking anything.
The economics worked out too. High-volume scans run in parallel and share the cached prefix, while the single-instance final check stays isolated. The place where inheritance pays off and the place where independence is required never overlapped to begin with.
What to check next
Open .claude/agents/ and pick one agent that handles review or verification. Then look at your recent session logs and confirm whether you actually named it when you delegated. If you were leaving the name off, that agent has probably already shifted to the rubber-stamp side.
Redrawing the line from there is still early enough.
I trusted my own review step right up until I understood what the default change had done to it. If this saves someone else the same blind spot, I am glad. Thank you for reading.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.