Spun off from #946, whose AC3 reads "running pytest tests/ leaves os.environ unchanged". #946 fixes the mechanism it cites — tests/e2e/cli/conftest.py copying ANTHROPIC_API_KEY out of .env at import time. A second, separate mechanism remains.
Problem
codeframe/cli/app.py:42 calls load_env_files() at module import, not inside a command callback:
from codeframe.core.env_provenance import load_env_files # noqa: E402
load_env_files()
So any test that does from codeframe.cli.app import app — which is most of the CLI test suite — loads the repository's .env into the ambient os.environ for the remainder of the session. Reproduced with the key explicitly unset:
$ env -u ANTHROPIC_API_KEY .venv/bin/python -c "
import os
from codeframe.cli.app import app
print('after cli app import:', bool(os.environ.get('ANTHROPIC_API_KEY')))"
Ignoring 1 security-sensitive key(s) from the repository's .env: AUTH_SECRET
after cli app import: True
Why this is P2 and not P1
The behaviour is correct for the CLI — cf is supposed to read the user's .env, and #904 already hardened which keys it will accept and made the repo's file lose to the operator's environment. Nothing is leaked outside the machine. The defect is one of scope: it happens on import, so it also applies to a process that never intends to run a command.
The concrete consequence is the same one #946 names for the conftest: requires_api_key-gated tests silently flip from skip to run, and any test asserting on an absent key has to defend itself with monkeypatch.delenv.
Acceptance criteria
Evidence
Spun off from #946, whose AC3 reads "running
pytest tests/leaves os.environ unchanged". #946 fixes the mechanism it cites —tests/e2e/cli/conftest.pycopyingANTHROPIC_API_KEYout of.envat import time. A second, separate mechanism remains.Problem
codeframe/cli/app.py:42callsload_env_files()at module import, not inside a command callback:So any test that does
from codeframe.cli.app import app— which is most of the CLI test suite — loads the repository's.envinto the ambientos.environfor the remainder of the session. Reproduced with the key explicitly unset:Why this is P2 and not P1
The behaviour is correct for the CLI —
cfis supposed to read the user's.env, and #904 already hardened which keys it will accept and made the repo's file lose to the operator's environment. Nothing is leaked outside the machine. The defect is one of scope: it happens onimport, so it also applies to a process that never intends to run a command.The concrete consequence is the same one #946 names for the conftest:
requires_api_key-gated tests silently flip from skip to run, and any test asserting on an absent key has to defend itself withmonkeypatch.delenv.Acceptance criteria
load_env_files()runs when a command executes, not at module import (a Typer@app.callback(), or lazily inside the code paths that read the values)from codeframe.cli.app import appwithANTHROPIC_API_KEYunset leaves it unset — asserted in a child process so the test cannot poison itselfcfinvoked from a directory with a.envstill picks the values up: the existing [P0.10] Stop a repository .env from overriding the operator's environment #904 precedence tests (operator env wins, security-sensitive keys refused) still pass unchangedcodeframe/cli/validators.py, the otherload_env_filescaller, is checked for the same import-time patternEvidence
codeframe/cli/app.py:42codeframe/core/env_provenance.py:241(load_env_files)tests/test_offline_default_946.py— the [P1.28] Make an offlineuv run pytesttruly offline #946 regression test, scoped to the conftest mechanism only, with this one called out as a known limitation