feat: add fan-out budgets and auto-mode/workflow settings keys - #16
Merged
intech merged 2 commits intoAug 3, 2026
Merged
Conversation
Four env vars baked into the image (all overridable from .env, since
`docker -e` wins over image ENV):
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS=12 (upstream default 20)
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION=100 (upstream default 200)
CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSION=100 (upstream default 200)
CLAUDE_CODE_RETRY_WATCHDOG=1
The concurrency number is measured, not guessed. Inside this image: 4 processes
before `claude`, ~11 with `claude` plus the four MCP servers attached, peaking at
15 with three parallel Bash tool calls - roughly 1-2 PIDs per in-flight tool call
over an ~11-PID floor. At the upstream 20 a fan-out where every subagent holds a
shell lands close enough to the --pids-limit=100 cgroup cap that the container,
not Claude Code, decides what fails. 12 leaves ~40 PIDs of headroom.
The two per-session counters are cost/runaway guards rather than PID guards, so
100 is a deliberately conservative choice, not a measured ceiling. Their lower
bound is 1: a value of 0 is rejected by validation and silently falls back to the
upstream default. That is documented in CLAUDE.md and .env.example because "set
it to 0 to disable" is the obvious wrong guess.
Three settings.json keys:
autoMode.classifyAllShell: true - route every shell command through the
auto-mode classifier (upstream false)
agentPushNotifEnabled: true - proactive phone push once Remote Control
connects (upstream false)
workflowSizeGuideline: "small" - advisory dynamic-workflow size; accepted
values unrestricted|small|medium|large,
upstream default medium
Verification, stated honestly rather than uniformly:
- Behaviourally verified: the session counters. With
CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSION=1 the first WebSearch succeeds and the
next two are refused with "this session has used its web search budget (1 of 1
WebSearch calls)". A value of 0 was ignored, which is how the lower bound was
found. CLAUDE_CODE_RETRY_WATCHDOG is read through the boolean env parser that
accepts 1/true/yes/on.
- Accepted but effect not observed: classifyAllShell, agentPushNotifEnabled,
workflowSizeGuideline. They are in 2.1.220's settings-key table, their values
come from the binary's own enum/parser, and `claude doctor` reports no invalid
settings - but the workflow-size system reminder is not injected in -p/headless
runs, and agentPushNotifEnabled cannot act until Remote Control connects, which
needs `claude auth login`.
- Rebuilt image: build exits 0, baked ENV present, ~/.claude/settings.json carries
all keys, `claude doctor` clean, all four MCP servers Connected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two corrections to the previous commit's documentation.
1. The "0 falls back to the upstream default because validation rejects it"
claim asserted a mechanism that was never observed. What was actually seen is
that with either per-session counter set to 0 the action still ran - i.e. 0
behaves as if the variable were unset. The practical warning is unchanged
("0 does not disable these, use 1"); the invented explanation is gone.
2. CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS is now behaviourally verified rather
than inferred from a shared read path. With the value set to 1, three
parallel Agent calls in one message produce one success and two refusals:
Concurrent subagent limit reached. You can run 1 subagents at once. Do not
retry. If the user wants more concurrent subagents, ask them to increase
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS.
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION remains the one counter inferred from
the shared read path rather than exercised, and CLAUDE.md now says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #15, which is stacked on #14. Merge order: #14 → #15 → this. Retarget the base to
mainonce the parents land.What
Adds the runaway-fan-out budgets and the auto-mode / workflow settings keys that appeared between claude-code 2.1.183 and 2.1.220.
Baked env vars (all overridable from
.env—docker -ewins over imageENV)CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTSCLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONCLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSIONCLAUDE_CODE_RETRY_WATCHDOGThe concurrency number is measured, not guessed. Inside this image: 4 processes before
claude, ~11 withclaudeplus the four MCP servers attached, peaking at 15 with three parallel Bash tool calls — roughly 1–2 PIDs per in-flight tool call over an ~11-PID floor. At the upstream 20, a fan-out where every subagent holds a shell lands close enough to the--pids-limit=100cgroup cap that the container, not Claude Code, decides what fails. 12 leaves ~40 PIDs of headroom.The two per-session counters are cost/runaway guards rather than PID guards, so 100 is a deliberately conservative choice, not a measured ceiling.
settings.jsonkeysautoMode.classifyAllShelltrue— every shell command goes through the auto-mode classifier, not only arbitrary-code-execution patternsfalseagentPushNotifEnabledtrue— proactive phone push once Remote Control connectsfalseworkflowSizeGuideline"small"— advisory dynamic-workflow size (unrestricted|small|medium|large)mediumworkflowSizeGuideline: "small"is the one judgement call here rather than a safety measure — flag it if you'd rather keepmedium.Verification — stated per key, not uniformly
Behaviourally verified — the two that matter most. With
CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSION=1, the first WebSearch succeeds and the next two are refused with the CLI's own message:With
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS=1, three parallel Agent calls in one message produce one success and two refusals:Inferred, not exercised.
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONshares the read path of the two above.CLAUDE_CODE_RETRY_WATCHDOGis read through the boolean env parser that accepts1/true/yes/on.Accepted, but effect not observed.
classifyAllShell,agentPushNotifEnabled,workflowSizeGuidelineare in 2.1.220's settings-key table, their values come from the binary's own enum/parser, andclaude doctorreports no invalid settings — but the workflow-size system reminder is not injected in-p/headless runs, so it could not be confirmed end to end, andagentPushNotifEnabledcannot act until Remote Control actually connects (which needsclaude auth login— see #15).Rebuilt image. Build exits 0; baked ENV present;
~/.claude/settings.jsoncarries every key;claude doctorclean; all four MCP servers ✔ Connected at the defaultMCP_TIMEOUT=10000.Not verified locally: arm64 (no qemu binfmt on the build host) — covered by the
pull_requestCI job.🤖 Generated with Claude Code