python-gitreins-evaluator-401-env-loss
Wrote the solution to ~/gitreins-401-env-loss-solution.md (17 KB, self-contained).
Root cause. engine/llm.py resolves the endpoint env-only:
os.getenv("GITREINS_LLM_BASE_URL") or "https://api.openai.com/v1".
Scheduler/cron workers inherit no GITREINS_LLM_* (creds live only in ~/.hermes/.env), so the DeepSeek key is sent to OpenAI → 401.
Key insight verified empirically. A venv-local sitecustomize.py is shadowed: /usr/lib/python3.14 precedes the venv site-packages on sys.path, so /usr/lib/python3.14/sitecustomize.py (apport) wins. A .pth executable import line in site-packages runs at interpreter startup regardless and is the correct hook.
Fix in the doc:
1. ~/.hermes/.env (mode 600) as source of truth.
2. _gitreins_env_bootstrap.py (idempotent, never raises, os.environ.setdefault so env wins, explicit GITREINS_ENV_FILE overrides default).
3. Installer that discovers every venv carrying engine.llm/gitreins and drops zzz_gitreins_env.pth into its site-packages.
4. gitreins-env shell wrapper for non-Python launch paths.
5. Optional durable fix: add a dotenv config hook in engine/llm.py.
Verification evidence produced here on a faithful LLMClient reproduction plus the real interpreter:
- before: https://api.openai.com/v1/chat/completions
- after .pth: https://api.deepseek.com/v1/chat/completions
- explicit process env still wins
- clean-env round-trip: task complete: pong with POST /v1/chat/completions auth=Bearer sk-deepseek-realkey (HTTP 200)
Caveat: this sandbox had no live gitreins venv (~/.local/share/pipx/venvs/gitreins broken/absent) and no systemd (systemctl: offline). The .pth/shadowing mechanics and the full client round-trip were verified directly; the systemd-run full-task commands are documented for the real host, including the RuntimeMaxSec/--scope handling for the sandbox's >7-min child reaper.
# Evidence - Problem class: python-gitreins-evaluator-401-env-loss - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T19:06:17.551Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "gitreins task complete 401 Unauthorized on api.openai.com with a deepseek key: engine/llm.py LLMClient resolves base_url as os.getenv(GITREINS_LLM_BASE_URL) or https://api.openai.com/v1 \u2014 env-only, no config hook. Cron/worker sessions inherit no GITREINS_LLM_* vars (creds only in ~/.hermes/.env) so the deepseek key goes to openai. Diagnosis: check resolved chat_url via LLMClient._chat_url; grep for the base_url fallback in the installed engine. Fix: env-bootstrap wrapper (setdefault semantics, env wins) + .pth import-line module in each gitreins-carrying venv (sitecustomize.py is shadowed by /usr/lib/pythonX/sitecustomize.py in venvs \u2014 use a .pth). Prove with clean-env LLMClient round-trip to the resolved chat_url, then a full gitreins task complete under env -i via systemd-run (agent sandbox TERMs >7min children).", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-gitreins-evaluator-401-env-loss", "provider": "openrouter", "solved_at": "2026-09-13T19:06:17.551Z", "version": ""}Wrote the solution to ~/gitreins-401-env-loss-solution.md (17 KB, self-contained).
Root cause. engine/llm.py resolves the endpoint env-only:
os.getenv("GITREINS_LLM_BASE_URL") or "https://api.openai.com/v1".
Scheduler/cron workers inherit no GITREINS_LLM_* (creds live only in ~/.hermes/.env), so the DeepSeek key is sent to OpenAI → 401.
Key insight verified empirically. A venv-local sitecustomize.py is shadowed: /usr/lib/python3.14 precedes the venv site-packages on sys.path, so /usr/lib/python3.14/sitecustomize.py (apport) wins. A .pth executable import line in site-packages runs at interpreter startup regardless and is the correct hook.
Fix in the doc:
1. ~/.hermes/.env (mode 600) as source of truth.
2. _gitreins_env_bootstrap.py (idempotent, never raises, os.environ.setdefault so env wins, explicit GITREINS_ENV_FILE overrides default).
3. Installer that discovers every venv carrying engine.llm/gitreins and drops zzz_gitreins_env.pth into its site-packages.
4. gitreins-env shell wrapper for non-Python launch paths.
5. Optional durable fix: add a dotenv config hook in engine/llm.py.
Verification evidence produced here on a faithful LLMClient reproduction plus the real interpreter:
- before: https://api.openai.com/v1/chat/completions
- after .pth: https://api.deepseek.com/v1/chat/completions
- explicit process env still wins
- clean-env round-trip: task complete: pong with POST /v1/chat/completions auth=Bearer sk-deepseek-realkey (HTTP 200)
Caveat: this sandbox had no live gitreins venv (~/.local/share/pipx/venvs/gitreins broken/absent) and no systemd (systemctl: offline). The .pth/shadowing mechanics and the full client round-trip were verified directly; the systemd-run full-task commands are documented for the real host, including the RuntimeMaxSec/--scope handling for the sandbox's >7-min child reaper.
# Evidence - Problem class: python-gitreins-evaluator-401-env-loss - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T19:06:17.551Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "gitreins task complete 401 Unauthorized on api.openai.com with a deepseek key: engine/llm.py LLMClient resolves base_url as os.getenv(GITREINS_LLM_BASE_URL) or https://api.openai.com/v1 \u2014 env-only, no config hook. Cron/worker sessions inherit no GITREINS_LLM_* vars (creds only in ~/.hermes/.env) so the deepseek key goes to openai. Diagnosis: check resolved chat_url via LLMClient._chat_url; grep for the base_url fallback in the installed engine. Fix: env-bootstrap wrapper (setdefault semantics, env wins) + .pth import-line module in each gitreins-carrying venv (sitecustomize.py is shadowed by /usr/lib/pythonX/sitecustomize.py in venvs \u2014 use a .pth). Prove with clean-env LLMClient round-trip to the resolved chat_url, then a full gitreins task complete under env -i via systemd-run (agent sandbox TERMs >7min children).", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-gitreins-evaluator-401-env-loss", "provider": "openrouter", "solved_at": "2026-09-13T19:06:17.551Z", "version": ""}