Summary
workflowControlChildEnvironment neutralizes BUILT_IN_PROVIDER_HOME_ENVIRONMENT_VARIABLES (including CLAUDE_CONFIG_DIR) by assigning the empty string:
const child: Record<string, string> = Object.fromEntries(
[
...CONTROLLER_ONLY_ENVIRONMENT_VARIABLES,
...BUILT_IN_PROVIDER_HOME_ENVIRONMENT_VARIABLES,
...providerCredentialEnvironmentVariables(source)
].map((name) => [name, ""])
);
An empty value is not equivalent to an unset variable. Claude Code treats an empty CLAUDE_CONFIG_DIR as absent and falls back to the current working directory for its config dir, rather than to ~/.claude.
Impact
When an agent runs without a forwarded config directory, the CLI writes its session state into the agent's own git worktree:
projects/<munged-cwd>/<session-id>.jsonl
projects/<munged-cwd>/<session-id>/tool-results/<id>.txt
Those files become part of the workspace patch. They are agent session transcripts, so this also means conversation state is written into a tree that is diffed, snapshotted, and handed to downstream nodes.
Combined with the 128-character WORKSPACE_PATCH_SEGMENT cap (filed separately), <munged-cwd> is the absolute worktree path with separators replaced, so the run fails non-deterministically depending on path length:
✗ verify:setup-foundry (attempt 1): workspace patch file path contains an unsafe path segment
✗ Run failed: Task failed: verify:setup-foundry
Two runs of the same project, differing only in the length of the run id, produced a 123-character segment (passed) and a 130-character segment (failed).
Suggested direction
Neutralizing by assignment is the right intent, but for variables whose absence is meaningful the child environment should omit the key rather than set it to "". Worth auditing the other entries in BUILT_IN_PROVIDER_HOME_ENVIRONMENT_VARIABLES (CODEX_HOME, KIMI_CODE_HOME, KIMI_SHARE_DIR) for the same CLI-dependent behavior.
I have not attempted a fix: this is the credential/environment isolation boundary and I could not validate a change to it across all providers.
Notes
- Reproduced on darwin/arm64 with
ClaudeAgent + auth = "subscription".
- Not macOS-specific in principle — the empty-vs-unset distinction applies anywhere.
- Encountered while making the config directory conditional; the correct resolution there was operational (register the config dir once via
CLAUDE_CONFIG_DIR=<dir> claude + /login, since Claude Code scopes the subscription credential per config directory). But the empty-string neutralization remains a latent trap for any code path that legitimately omits a provider home.
🤖 Generated with Claude Code
Summary
workflowControlChildEnvironmentneutralizesBUILT_IN_PROVIDER_HOME_ENVIRONMENT_VARIABLES(includingCLAUDE_CONFIG_DIR) by assigning the empty string:An empty value is not equivalent to an unset variable. Claude Code treats an empty
CLAUDE_CONFIG_DIRas absent and falls back to the current working directory for its config dir, rather than to~/.claude.Impact
When an agent runs without a forwarded config directory, the CLI writes its session state into the agent's own git worktree:
Those files become part of the workspace patch. They are agent session transcripts, so this also means conversation state is written into a tree that is diffed, snapshotted, and handed to downstream nodes.
Combined with the 128-character
WORKSPACE_PATCH_SEGMENTcap (filed separately),<munged-cwd>is the absolute worktree path with separators replaced, so the run fails non-deterministically depending on path length:Two runs of the same project, differing only in the length of the run id, produced a 123-character segment (passed) and a 130-character segment (failed).
Suggested direction
Neutralizing by assignment is the right intent, but for variables whose absence is meaningful the child environment should omit the key rather than set it to
"". Worth auditing the other entries inBUILT_IN_PROVIDER_HOME_ENVIRONMENT_VARIABLES(CODEX_HOME,KIMI_CODE_HOME,KIMI_SHARE_DIR) for the same CLI-dependent behavior.I have not attempted a fix: this is the credential/environment isolation boundary and I could not validate a change to it across all providers.
Notes
ClaudeAgent+auth = "subscription".CLAUDE_CONFIG_DIR=<dir> claude+/login, since Claude Code scopes the subscription credential per config directory). But the empty-string neutralization remains a latent trap for any code path that legitimately omits a provider home.🤖 Generated with Claude Code