Skip to content

nebula started from inside a Claude Code session no longer passes that session's identity to everything it starts, so Claude agents under it keep their transcripts - #120

Open
DrewBradfordXYZ wants to merge 1 commit into
AgentSystemLabs:mainfrom
DrewBradfordXYZ:scrub-agent-claude-env

Conversation

@DrewBradfordXYZ

@DrewBradfordXYZ DrewBradfordXYZ commented Oct 4, 2026 •

Copy link
Copy Markdown

What happens

When Claude Code runs a tool, the shell it uses carries variables that name that session: CLAUDECODE, CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_CHILD_SESSION, CLAUDE_PID, its messaging socket and token, and a few more. Any nebula daemon first started from such a shell keeps them, and every agent, terminal and claude probe it launches inherits them.

This doesn't take an unusual setup. Ask Claude, running in an ordinary terminal tab, to nebula add . a project and nebula's own autostart (spawn_daemon) starts the daemon from Claude's tool shell. spawn_daemon detaches it into a session of its own, but it passes the environment through as it is. The same happens when the daemon is restarted from an agent's shell.

docs/how-it-works.md already names this source for NO_COLOR — "exported by the agent shell the daemon was first started from" — and the daemon drops that variable before launching agents. The session variables do more damage than NO_COLOR. Each Claude agent nebula starts believes it is a sub-process of the session that started the daemon, and Claude Code (checked on 2.1.288) turns transcript saving off for an inherited CLAUDE_CODE_CHILD_SESSION. Its status line says "⚠ Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker". Claude Code's env-var docs describe the same misclassification for "a background launcher first started by Claude Code's Bash tool". So:

  • these sessions write no .jsonl, and --resume finds nothing;
  • when the idle reaper stops a detached agent and the user comes back, the resume comes up as a fresh, empty session ("no Claude transcript for the session — spawning fresh"). A nebula worktree relocation does the same;
  • every agent also holds the other session's messaging socket and token.

To reproduce: from inside a Claude Code session, with no daemon running, have it run nebula add <repo>. Then launch a Claude agent in nebula, send it a prompt, and look in ~/.claude/projects/*/ for its .jsonl. It isn't there. The same claude run with those variables removed writes the transcript.

The change

Next to the NO_COLOR handling, at a wider scope: nebula drops these variables from its own environment first thing in main(), for every subcommand, before any thread or child exists. So nothing it starts inherits them: the daemon, its agents, terminals and probes, and the TUI's own helpers. Autostart is covered because the daemon re-runs main(). The daemon and TUI log which variables were dropped.

  • Only session identity is removed (nebula_core::env::HOST_CLAUDE_SESSION_VARS), not the whole CLAUDE_CODE_ prefix. User settings such as CLAUDE_CODE_USE_BEDROCK, an OAuth token and CLAUDE_CODE_SSE_PORT (an IDE terminal's link back to the editor) are kept.
  • Claude Code also sets GIT_EDITOR=true and COREPACK_ENABLE_AUTO_PIN=0 on its tool shells. In a nebula terminal, GIT_EDITOR=true makes a bare git commit abort on an empty message. Both are dropped only at exactly those values, and only when a session marker is also present, so a user who sets them deliberately keeps them.
  • Nothing in nebula reads any of the dropped variables, so the agent verbs (worktree, spawn, open, rename) behave as before.
  • A daemon that is already running keeps its environment until it is restarted.

Tests

  • Unit tests in nebula-core/src/env.rs for which variables are picked out (pure, no process-env mutation).
  • An e2e test, agents_never_inherit_the_claude_session_the_daemon_started_in in tests/e2e_pty.rs, starts a daemon with the session variables set and checks that the agent's environment has none of them while CLAUDE_CODE_USE_BEDROCK survives. With the one call in main() removed it fails with CLAUDECODE leaked.
  • cargo test --no-fail-fast -p nebula -p nebula-core -p nebula-daemon passes. The exceptions are tui_drag_past_the_pane_top_autoscrolls_and_copies_the_run and nebula_open_from_inside_a_session_raises_the_file_tabs, which fail the same way on an untouched main on my machine.

🤖 Generated with Claude Code

…t session's identity to everything it starts, so Claude agents under it keep their transcripts

A Claude Code session sets CLAUDECODE, CLAUDE_CODE_SESSION_ID,
CLAUDE_CODE_CHILD_SESSION, CLAUDE_PID, its messaging socket and token, and
a few more on the shells it runs tools in. A daemon started from such a
shell (`nebula` typed by an agent) kept them and passed them to every
agent, terminal and `claude` probe it started, so each one believed it was
a sub-process of that other session. Claude Code turns its transcript off
for an inherited CLAUDE_CODE_CHILD_SESSION ("Transcript saving is off —
inherited CLAUDE_CODE_CHILD_SESSION marker"), so those agents wrote no
transcript: --resume found nothing, and a `nebula worktree` relocation came
back as a fresh session. They also talked to the other session's messaging
socket.

`nebula` now drops those variables from its own environment first thing,
for every subcommand, before any thread or child exists, and logs which
ones it dropped. Only session identity goes. User settings that share the
CLAUDE_CODE_ prefix (CLAUDE_CODE_USE_BEDROCK, an OAuth token) stay, and so
does CLAUDE_CODE_SSE_PORT, an IDE terminal's link back to the editor.
Claude Code's own tool-shell overrides, GIT_EDITOR=true (which makes a
bare `git commit` in a nebula terminal abort) and
COREPACK_ENABLE_AUTO_PIN=0, are dropped only at exactly those values and
only beside a session marker, so a user who sets either keeps it.

A daemon already running keeps its environment until it restarts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant