Files
computer-playbook/utils/env
kevinveenbirkenbachandClaude Opus 5 6ecfccfc38 fix(tools): publish the model gateway in the lane and judge the lane by what it serves
The lane ran litellm but never republished its port, so a host-side probe hit the compose proxy on the same number and read 502. The lane could therefore never report ready, and the oracle suite skipped itself to a green exit instead of asking a judge. A handler reads the port from the role the way translate.py already does, the runner forwards it, and TOOLS_LANE_KEYS takes it up: compose_file_args layers the override in without checking its variables, so an .env written before the key existed would abort every compose command rather than merely drop the lane.

The gateway's master key is pinned in the lane inventory for the reason the System One key already is: the provisioner would otherwise generate it into a vault the checks cannot read back, and the oracle judge is not a deployed consumer with a virtual key of its own.

serving() stops treating reachability as readiness. A lane brought up before AI_LOCAL_MODELS named a different model keeps serving the old one, and a gateway holding a key these checks cannot use looks healthy from outside while refusing every call they make. It now asks /models with the bearer token and requires the configured alias, and requires the gateway to offer every language the catalogs name, because that advertisement is what the translation run waits on. Without this the lane waved itself through and no change to its roles ever shipped.

Two lane-scoped overrides come with it. The ollama role caps itself at four CPUs, which left the host at 361 percent while twenty cores idled, so the lane gives it the host count the way the engine entry above it already does. And the lane pins the router to its declared order: the decider may send a pair the engine serves to the chat model, which a batch over 183 catalogs cannot pay for. Deployments keep the decider.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-18 10:06:48 +02:00
..
…
…

utils/env 📐

This directory hosts the Python implementation behind make dotenv. For the general rules that govern READMEs in code directories see documentation.md. For the per-variable defaults that this code consumes see default.env.

Purpose 🎯

The package converts the committed default.env plus a small runtime context into the gitignored .env at the repo root. Every module here belongs to one of four roles: parse, resolve, orchestrate, write.

Structure 🗂️

File Role
parser.py Read default.env into (values, comments) tuples.
runtime.py Resolve host-context lookups (disk, RAM, hostname, GHA/Act flags, /proc/version, helper-script invocation).
builder.py Thin orchestrator that defines EnvBuilder, BuildContext, and build_env(). Walks the handler registry.
writer.py Serialise an EnvBuilder into a docker-compose-compatible .env on disk.
handlers/ One module per dynamically-computed variable. See handlers README.

The CLI entry point lives at __main__.py under cli/meta/env/. Shell consumers source load.sh under scripts/meta/env/.

File Naming 🏷️

  • Module names MUST stay lowercase snake_case.
  • Pure passthrough or simple parser/writer helpers MUST live directly under utils/env/.
  • Per-variable computation MUST live under handlers/ (see handlers README for the naming convention used there).
  • Test files live in tests/unit/python/utils/env/ and mirror the module name with a test_ prefix.

Machine Facts vs Run Facts 🧭

The writer a handler picks decides how long a value lives. Both kinds are registered in default.env and both are visible to the lints; they differ only in whether the generated .env remembers what the environment said.

Writer Meaning On the next make dotenv
eb.setdefault machine fact a non-empty value in the process env is adopted and written back
eb.set run fact recomputed from BuildContext; whatever the process env said is discarded

setdefault is the operator-override contract: exporting a key changes it and the change persists. That persistence is why it MUST NOT carry a value an environment declares for a single run. Makefile exports BASH_ENV, so every make recipe sources .env — a value adopted once is re-exported into every later recipe and into every container that mounts the repo, and a per-run decision has silently become a property of the machine.

A key that some environment declares per run therefore MUST be written by a dedicated handler through eb.set. The exporting shell still wins for its own run, because load.sh preserves non-empty caller values when it sources .env; the file keeps the derived default. INFINITO_RUNNING_ON_GITHUB and INFINITO_CACHE_STACK are written this way. Note the consequence: for those keys .env and the running shell can legitimately disagree, and .env is not the authority.

Import Rules 🔗

  • parser.py, runtime.py, and writer.py MUST NOT import from builder.py or handlers/.
  • builder.py MUST NOT import individual handler modules; it MUST go through the ORDERED_HANDLERS list exposed by handlers/__init__.py.
  • Handler modules MUST NOT import each other (see handlers README).