Clarifying Codex command environment contracts #26901
royenheart
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
1. Problem Summary
I would like to collect several related Codex command-environment threads and
discuss what contract would make this behavior easier to reason about.
This area is currently split across many issue, PR, and Discussion threads:
shell_environment_policycontrols theinitial environment map passed to command subprocesses, but command
execution can still run through a login shell. That shell may read profile
files and rewrite
PATH, activate a different Python, reintroduce filteredvariables, or otherwise mutate the environment after Codex has constructed
it. This is the common thread behind Bug Report: PATH ordering is mutated when Codex shells launch via bash -lc #4210, Codex runs commands via bash --login and drops per-project env #4843, Login shells override inherited PATH in curated environments; need configurable default #8922, Proposal: Add [shell] default_login config to fix PATH clobbering in curated environments #11952, feat: add config
allow_login_shell#12312,and Clarify the interaction between
allow_login_shellandshell_environment_policyin documentation #14774.not always the environment of the terminal that just launched the visible UI.
It can come from the launcher process, a reused local app-server daemon, or a
remote exec server. Build remote exec env from exec-server policy #17216 fixed one remote case by deriving remote exec env
from the exec server; Fix: TUI starting in wrong CWD #23538 fixed an adjacent local daemon cwd issue; app-server local command execution lacks a cwd-scoped environment contract #24638
remains the open local env-provenance issue.
scripts are lifecycle mechanisms, not durable command-env providers. A setup
script can prepare files or run tools, but shell-local state such as
export FOO=bardoes not automatically become part of later shell commands.Set environment variable through Local environments #10543, New handoff to worktree doesn't use environments #18981, and Codex App Remote SSH new worktree does not run local environment setup script #23648 show related setup/worktree drift.
filesystem, shell, setup script, skills, and dependency environment may live
on the remote host rather than the local Desktop/IDE client. Improve Codex Desktop support for SSH remote workspaces #21509 asks for
clearer Remote SSH workspace bootstrap; Codex App Remote SSH new worktree does not discover repo-local skills #22545 and Codex App Remote SSH new worktree does not run local environment setup script #23648 show that new
remote worktree flows can lose repo-local context/setup.
project interpreter or virtual environment. Codex ignores VSCode selected Python interpreter / virtual environment #23642 reports that VS Code's
selected Python interpreter is not reflected in Codex command execution,
which is another example of "workspace looks right, command env is wrong."
SessionStarthook overlays. Hooks can currently contribute context,but current
maindoes not expose a supported structured env overlayfrom
SessionStart. Add CODEX_ENV_FILE env persistence #24468, Add CODEX_ENV_FILE hook persistence #24650, Add CODEX_ENV_FILE for SessionStart hooks #24805, and Add SessionStart hook environment overlays #25364 explored env-file andstructured-overlay designs, but none of those changes are merged into main.
clearest open issue for declaring that commands in a workspace should run
inside a project environment. It intentionally frames tools such as
direnv,development shells, language version managers, and containers as examples of
a generic provider problem rather than one-off command-prefix instructions.
It would be useful to discuss a command-environment contract that can answer:
The common symptom is simple: the cwd is correct, but
python,pytest,cargo,pnpm,kubectl, or a repo-local helper resolves from the wrongenvironment.
2. Motivating Examples
The examples below use anonymized local paths and environment names. They are
intended to show the shape of the failure.
2.1 Launcher environment vs login shell
A user starts Codex from a shell where the project Python environment is active:
The launcher sees:
With the default shell behavior observed in this repro, Codex command execution
used
/bin/bash -lc .... On my machine that login shell initializedbase,so the command saw:
Running the same probe with
-c allow_login_shell=falsechanged the commandshell to
/bin/bash -c ...and preserved the launcher environment:This is not just a Python question. It is the same class of issue as PATH
ordering, profile startup, and inherited-env clobbering in #4210, #4843, and
#8922.
Reproduction script:
2.2 Setup exports do not persist
In a two-command Codex probe, the first command exported a variable and the
second command printed it:
The second command printed:
This is expected if commands run as separate shell processes, but it means setup
scripts cannot be treated as implicit persistent command environments. If setup
is only setup, Codex needs a separate explicit contract for durable command env.
Reproduction script:
2.3 Workspace cwd is not a project command environment
Setting the cwd to a workspace does not automatically materialize that
workspace's development environment. For example, a project may have:
A bare Codex command in that cwd observed empty project variables and did not
find the repo-local helper. The same command wrapped in the provider did:
The gap is that the provider step is not represented in the command execution
contract. Today the model or user must remember provider-specific wrappers for
each command. A provider-level contract could make the active project command
environment visible to Codex instead of only appearing inside prompt
instructions.
Reproduction script:
2.4 Local app-server env provenance
#24638 shows that visually similar local TUI launches can observe different env
sources:
The point is provenance. It would help if Codex made explicit whether command
env came from the launcher, a reused local daemon, a remote executor, or a
declared project provider.
Manual reproduction recipe:
2.5 No merged
SessionStartenv overlayCurrent
mainhas hook output support for context, but not a supportedSessionStartenv overlay:codex-rs/hooks/src/engine/output_parser.rs:SessionStartOutputcontainsuniversal output plus
additional_context.codex-rs/hooks/src/schema.rs: hook output wire types usedeny_unknown_fields.codex-rs/hooks/schema/generated/session-start.command.output.schema.json:additionalPropertiesis false andhookSpecificOutputcontainshookEventNameand optionaladditionalContext, but noenv.Question: should hooks have a supported structured way to publish env data to
later commands? If yes, it should be explicit in schema/runtime behavior and
should not require replaying hidden shell scripts after command approval.
Reproduction script:
Expected output on the current
codex execpath:3. Current State
The current behavior has four separable layers:
shell_environment_policy,inherit = "all",allow_login_shell, andshell_command.login.shell startup mutations obvious to users.
fixed a local daemon cwd provenance regression.
launcher-vs-daemon-vs-remote env ownership.
remote setup flows still have inconsistent behavior.
a project environment to commands.
One possible way to keep the discussion scoped is to separate lower-level env
provenance from higher-level project providers: clarify provenance first, then
discuss whether a structured env overlay should exist, then decide whether
SessionStarthooks and project providers should produce that overlay.3.1 State Map
flowchart LR user["Workspace expects a project command environment"] --> runner["Codex shell command runner"] runner --> startup["Layer 1<br/>shell startup semantics"] runner --> provenance["Layer 2<br/>execution process provenance"] runner --> lifecycle["Layer 3<br/>local/worktree setup lifecycle"] runner --> provider["Layer 4<br/>project command environment provider"] startup --> shipped_login["Shipped<br/>allow_login_shell<br/>#12312"] startup --> open_login["Open design/docs gap<br/>#4210 #4843 #8922 #11952 #14774"] provenance --> shipped_remote["Shipped<br/>remote executor env source<br/>#17216"] provenance --> shipped_cwd["Shipped<br/>local daemon cwd fix<br/>#23538"] provenance --> open_env_source["Open gap<br/>local daemon env source<br/>#24638"] lifecycle --> shipped_local_env["Existing feature<br/>local environments / setup scripts"] lifecycle --> open_lifecycle["Open gaps<br/>exports/setup/worktree drift<br/>#10543 #18981 #23648"] provider --> umbrella["Provider issue<br/>first-class command environments<br/>#25452"] provider --> overlay_prs["Prior overlay attempts<br/>#24468 #24650 #24805 #25364"] open_env_source --> provenance_question["Question<br/>make env provenance explicit"] overlay_prs --> overlay_question["Question<br/>structured ThreadShellEnvOverlay"] overlay_question --> sessionstart_question["Question<br/>SessionStart produces overlay"] overlay_question --> provider_question["Question<br/>provider adapters"] umbrella --> provider_question provider_question --> fallback_question["Question<br/>session/wrap modes when snapshot is insufficient"]3.2 Code Mechanisms
codex-rs/protocol/src/shell_environment.rs;codex-rs/core/src/spawn.rscreate_env()builds command env fromstd::env::vars()in the process doing execution, appliesshell_environment_policy, and addsCODEX_THREAD_ID.spawn.rsthen usesenv_clear()andenvs(env).codex-rs/core/src/tools/handlers/unified_exec.rs;codex-rs/core/src/tools/handlers/shell/shell_command.rsallow_login_shell=falserejects explicitlogin=true; omittedlogindefaults to the config value.codex-rs/tui/src/lib.rscan_reuse_implicit_local_daemon(...)refuses reuse when the daemon cannot adopt launch state, such as non-empty-coverrides.codexandcodex -c ...use different command env sources while the UI still looks local. #24638 is the open env-provenance version of the earlier cwd issue fixed by #23538.codex-rs/core/src/environment_selection.rs;codex-rs/app-server-protocol/src/protocol/v2/{thread,turn}.rs;codex-rs/app-server/README.mdthread/start.environmentsandturn/start.environments;EnvironmentManagerresolves environment ids and cwd.export FOO=barin setup does not become durable command env. Remote/worktree setup paths still have gaps.SessionStartSessionStartcan inject context/system messages through supported output fields.hookSpecificOutput.envorCODEX_ENV_FILEsupport exists onmain.codex-rs/core/src/shell_snapshot.rs3.3 Timeline
shell_environment_policy.main.inherit = "core").shell_environment_policy.main.shell_environment_policy.inherittoall.main.bash -lcmutatingPATHordering.main.shell_commandtool.main.loginparameter toshell_command.main.[shell].default_login.allow_login_shell, but did not land.exportdid not persist.shell_snapshot=truebroke rc/direnv handling.[shell].default_login.allow_login_shell.main.allow_login_shelland env policy interaction.main.main.CODEX_ENV_FILEenv persistence by parsing export-style updates.codexcan inherit old daemon env;codex -c ...can inherit launcher env.CODEX_ENV_FILEbefore later commands.SessionStart, sourced once, and captured an env diff.abhinav/codex-env-filebranch, notmain.hookSpecificOutput.envSessionStartoverlay.3.4 Prior Attempts
Several recent PRs explored ways for setup or hooks to publish environment
changes to later shell commands:
CODEX_ENV_FILEpersistence, including parsing orsourcing shell-written state.
SessionStart, sourcingonce, and capturing an env diff.
cmd.exesupport adds additional shell-specificcomplexity, but it landed only in the stacked env-file branch.
hookSpecificOutput.env, treating environmentchanges as data rather than hidden shell code.
Taken together, these attempts raise the question of whether Codex needs a
small structured data contract rather than hidden shell replay. Command-time
sourcing creates correctness, sandbox, cleanup, race, secret-handling, and
approval-boundary problems; a structured thread shell env overlay could make
those tradeoffs explicit.
4. What To Clarify
can tell where the active env came from.
environment once, without relying on the model to remember provider-specific
wrappers for every command.
of commands run inside that environment.
runs after the user approved a visible command.
app-server, Desktop/IDE, worktrees, and remote execution.
5. Open Questions
launched client/session, the reused app-server daemon, or another explicit
rule? If daemon reuse remains visible to command execution, where should that
provenance be exposed to users and agents? Related: Build remote exec env from exec-server policy #17216, Fix: TUI starting in wrong CWD #23538, app-server local command execution lacks a cwd-scoped environment contract #24638.
the right lower-level primitive for durable command env changes, or should
this be solved only at the provider layer? The key questions are precedence,
protected runtime variables, Windows env-name normalization, resume/clear
behavior, and subagent inheritance. Related: Add SessionStart hook environment overlays #25364.
SessionStartas an overlay producer. If a structured overlay exists,should
SessionStarthooks be allowed to publish env data to later commands?The prior attempts suggest this should be structured data, not an env file
that is sourced before later approved commands. Related: Add CODEX_ENV_FILE env persistence #24468, Add CODEX_ENV_FILE hook persistence #24650,
Add CODEX_ENV_FILE for SessionStart hooks #24805, [codex] Support cmd.exe session env files #25128, Add SessionStart hook environment overlays #25364.
environments live inside existing local environment/worktree configuration,
or should they be a new config concept? Should the first supported mode be an
env snapshot, a persistent session, command wrapping, or a combination?
Related: Add first-class project command environments for agent shell execution #25452.
Add first-class project command environments for agent shell execution #25452, or would maintainers prefer a dedicated Discussion thread that
connects provenance, setup lifecycle, hooks, app-server, Desktop/IDE, and
remote execution?
6. Note and Thanks
I have run into several of the environment issues above in real work, and they
have been difficult to reason about because the related behavior is spread
across several execution paths and lifecycle boundaries.
Hope this discussion can provide a useful reference for contributors and help
improve related functions, to make Codex smoother for users. If anything here is
inaccurate, incomplete, or missing important prior context, please point it out directly.
7. References
Shell/login behavior:
allow_login_shell#12312allow_login_shellandshell_environment_policyin documentation #14774Historical implementation context:
shell_environment_policyconfiguration option #1249App-server / provenance:
Local/worktree/remote environments:
shell_snapshot=truebreaks rc handling such asdirenv, affecting ability to use Codex app's worktrees #11223SessionStart env overlay attempts:
Project command environment provider issue:
Beta Was this translation helpful? Give feedback.
All reactions