anthropics / anthropics/claude-code
[Bug] Subagent spawn depth controlled by undocumented remote feature flag, can change without release
- Vorherrschende Sprache
- Python
- Sterne
- 145k
- Forks
- 23.1k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
### Environment
- CLI 2.1.247, macOS darwin-arm64, Max 20x, main loop Opus 5
### What I found
`CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH` controls how many levels of subagents can spawn beneath a subagent. Reading the resolution logic out of the 2.1.247 binary:
```
e = env.CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH; if (e !== undefined) return e;
...
t.maxSubagentSpawnDepthFromGrowthBook = getFeatureValue_CACHED_MAY_BE_STALE(
"tengu_hazel_trellis", Psn
);
...
var Psn = 3
```
So the order is: environment variable, then a **remotely served GrowthBook feature flag** (`tengu_hazel_trellis`), then a hardcoded fallback of **3**.
Three consequences follow from that:
1. **The effective value can change without a release.** It is a server-side flag, so it is not pinned to a version the user installed and does not appear in any changelog. This is consistent with what was observed from the outside: 2.1.217 behaved as depth 1, 2.1.219 behaved as depth 3. From here that reads like a version change. It may just as easily have been the flag moving.
2. **Nothing in the product shows the active value.** There is no output in `/status`, `/context`, or the session header stating how deep spawning is currently permitted. The value is read through a function whose own name says `CACHED_MAY_BE_STALE`.
3. **It is undocumented.** No shipped documentation on this machine names the variable, and a search of this repo returns zero issues mentioning it. A user cannot look it up, and cannot discover it exists unless they read the binary.
### Why it matters
Depth 3 means a subagent can spawn subagents that spawn subagents. A subagent is already invisible while it runs, and per #82104 there is no live spend readout and no per-agent cap, so a three-level tree is invisible work under invisible work with nothing bounding it. The combination is how 750k tokens land after a kill in that issue, and how the 1.37B tokens of agent re-reads in my comment there accumulate without anyone noticing.
An execution parameter that determines the branching factor of unattended, unmetered work is not a good candidate for a silent remote default.
### Ask
1. **Show the resolved value.** Print the active spawn depth wherever the session reports its configuration, along with where it came from: environment, remote flag, or fallback.
2. **Document the variable**, including that a remote flag can override the fallback.
3. **Default to 1.** Nesting should be opt-in. If depth 3 is intentional, say so somewhere the user can read.
4. Log a line when the remote value differs from the fallback, so a change is at least observable after the fact.
Same reasoning applies to `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS`, which is also environment-then-default and also undocumented.
### Workaround
Set it explicitly, since the environment variable is checked first and wins over the flag:
```json
{ "env": { "CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "1",
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "6" } }
```
That is the only way to know what the value is, because setting it is the only way to stop it being decided elsewhere.
Beitragsleitfaden
Für dieses Repository ist kein Beitragsleitfaden indexiert
Rechercherichtung
No file paths or tests are named. Start by finding where the CLI resolves CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH and CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS, then where /status, /context, session headers, logging, and shipped docs are produced. Done means the resolved value and source are visible or logged as requested, the variable is documented, and the default behavior is agreed with maintainers.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- cli, documentation, observability
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 45/100