You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A claude --bg agent does not inherit the environment of whoever spawned it, so HELM_DEFAULTS_SUITE and HELM_MAIL_DIR do not reach it — and an isolated helm's background agent claims its mailbox in the operator's live ~/.helm/mail.
Measured in spike #330, from an agent tool call inside a real helm pane. Claude Code 2.1.226.
What was measured
Three --bg workers were started with three differentHELM_MAIL_DIR values. All three claimed in the first one. One of them also reported seeing the spawner's HELM_PANE, which it was never given.
The cause is architectural rather than a bug in the flag. claude --bg does not fork a fresh process from the caller: it hands the job to a supervisor daemon reparented to launchd, which keeps pre-warmed bg-pty-host spares already running. Ancestry, measured:
bg-spare ← bg-pty-host ← claude daemon run ← launchd(1)
The spare's environment was captured when the daemon started. Only argv is per-job. So every environment variable a --bg agent sees belongs to whichever session happened to start the daemon — which is also why spawn is sub-second and why nothing can orphan it.
Why this matters here
This is #285 arriving through a door #285's fix cannot reach. That ticket existed because a throwaway instance under HELM_DEFAULTS_SUITE claimed mailboxes in the operator's live root — addressable by his real agents, listed to them, and swept by the reaper on every session start. It was fixed by making the mailbox follow the suite, and the fix works: helm declares the decided suite into every pty child (PaneEnvironment.suiteDeclaration) and both writers resolve it.
A --bg agent is not a pty child of helm. Nothing helm declares reaches it. So the isolation promise — "no reachable path to the operator's state" — is false again for this one spawn path, and the mechanism that made it true cannot be pointed at the problem.
It also breaks the thing that makes capability testing safe: every spike run today was safe against a live machine because the suite isolates. A spike that reaches for --bg loses that without being told.
What to decide
Can helm influence the daemon's environment at all? If the daemon is started on first use, a helm that starts it deliberately — with the suite already set — would give every subsequent job the right root. Worth measuring: does the daemon exit when idle (the spike observed idle_exit after 1394 s), and does the next job start a fresh one that would capture a new environment?
Is there a per-job lever? Only argv is per-job today. --settings is argv and already carries other decisions — determine whether anything in it can set or override what the mailbox writers read, or whether the writers should learn to read a flag as well as an env var.
Or is the honest answer that --bg is not usable where isolation is required? That is a legitimate outcome, and if so it belongs in AGENTS.md beside the suite's own promise, which currently reads as unconditional.
Acceptance
Either a --bg agent spawned under a suite claims under that suite, or AGENTS.md's isolation promise names --bg as the exception, with the reason.
Whatever lands is measured from an agent tool call, not from a shell — the spike's finding was only visible because it ran from the seat that has the problem.
Related: #285 (the mailbox following the suite), #330 (the spike), #315 (the fix this bypasses).
A
claude --bgagent does not inherit the environment of whoever spawned it, soHELM_DEFAULTS_SUITEandHELM_MAIL_DIRdo not reach it — and an isolated helm's background agent claims its mailbox in the operator's live~/.helm/mail.Measured in spike #330, from an agent tool call inside a real helm pane. Claude Code 2.1.226.
What was measured
Three
--bgworkers were started with three differentHELM_MAIL_DIRvalues. All three claimed in the first one. One of them also reported seeing the spawner'sHELM_PANE, which it was never given.The cause is architectural rather than a bug in the flag.
claude --bgdoes not fork a fresh process from the caller: it hands the job to a supervisor daemon reparented to launchd, which keeps pre-warmedbg-pty-hostspares already running. Ancestry, measured:The spare's environment was captured when the daemon started. Only argv is per-job. So every environment variable a
--bgagent sees belongs to whichever session happened to start the daemon — which is also why spawn is sub-second and why nothing can orphan it.Why this matters here
This is #285 arriving through a door #285's fix cannot reach. That ticket existed because a throwaway instance under
HELM_DEFAULTS_SUITEclaimed mailboxes in the operator's live root — addressable by his real agents, listed to them, and swept by the reaper on every session start. It was fixed by making the mailbox follow the suite, and the fix works: helm declares the decided suite into every pty child (PaneEnvironment.suiteDeclaration) and both writers resolve it.A
--bgagent is not a pty child of helm. Nothing helm declares reaches it. So the isolation promise — "no reachable path to the operator's state" — is false again for this one spawn path, and the mechanism that made it true cannot be pointed at the problem.It also breaks the thing that makes capability testing safe: every spike run today was safe against a live machine because the suite isolates. A spike that reaches for
--bgloses that without being told.What to decide
idle_exitafter 1394 s), and does the next job start a fresh one that would capture a new environment?--settingsis argv and already carries other decisions — determine whether anything in it can set or override what the mailbox writers read, or whether the writers should learn to read a flag as well as an env var.--bgis not usable where isolation is required? That is a legitimate outcome, and if so it belongs inAGENTS.mdbeside the suite's own promise, which currently reads as unconditional.Acceptance
--bgagent spawned under a suite claims under that suite, orAGENTS.md's isolation promise names--bgas the exception, with the reason.HOMEredirected, and assert the shared~/.helm/mailwas never created.Related: #285 (the mailbox following the suite), #330 (the spike), #315 (the fix this bypasses).