Follow-up to mando-cli-simulator-runtime-2026-05-30. The §A env contract there has been superseded to match the real orchestrator image (simulator:1.11.0-feat.2569583284.7d69a070) and the real runner image (shape f7aa42f). Landed as c3f0af7 on feature/simulator-runtime. 8 files, +440 / −67. 813 tests passing, no new clippy warnings. Sole author andras.lederer, no co-author trailer.
The old §A names (POSTGRES_*, SIMULATOR_DB_SCHEMA, SIMULATOR_RUNNERS CSV, orchestrator-owned SIMULATOR_<NAME>_REPO_URL) are GONE. Use the contract below.
replaces the SIMULATOR_RUNNERS CSV. Default: simulator-<name>:<port>
SIMULATOR_<NAME>_REPO_URL/BRANCH/COMMIT_HASH
moved to runners — orchestrator no longer tells runners what to clone
Runner (per simulator-<name> container)
Healthcheck reverted to curl (was Python in §A).
GUROBI_LIC is reverted to an env var, not a file mount (was GRB_LICENSE_FILE mount in §A).
SIMULATION_MANDO_HOST / SIMULATION_MANDO_PORT now generalized to all six runners (was orchestrator-only routing).
Container reads unprefixedSIMULATOR_REPO_URL / SIMULATOR_BRANCH / SIMULATOR_COMMIT_HASH from its own env.
The workspace .env keeps the prefixed override convention: each runner service is rendered with SIMULATOR_REPO_URL: "${SIMULATOR_<NAME>_REPO_URL:-<default>}", so a single .env line per runner still overrides.
Open assumption — verify against the runner image when it lands
The renderer ASSUMES the runner image reads unprefixed SIMULATOR_REPO_URL/BRANCH/COMMIT_HASH. If the actual runner reads the prefixed SIMULATOR_<NAME>_* names directly, flip the renderer (the .env override convention stays).
filter_extras in src/runtime/templates.rs (line ~116) filters any workspace .env key against a managed-set const MANAGED_SIM_ENV_KEYS (line ~56). Survivors are auto-injected as KEY: "${KEY}" into every simulator service (orchestrator + all six runners). Keys are sorted alphabetically — deterministic output.
Each injection emits tracing::info!(env_var, simulator, "passing env var to simulator") (one per (key, service) pair) — visible with RUST_LOG=info.
src/workspace/bootstrap.rs::ensure_all_generated reads .env via Adapter::Dotenv and threads the filtered set into SimulatorGenCtx.
Rule: if you add a new managed key to the simulator templates, also add it to MANAGED_SIM_ENV_KEYS, otherwise the user’s .env value will be double-injected (managed slot + extras slot).
sim-postgres Aliasing Pattern (broadly reusable)
Pattern: route shared defaults through the orchestrator's env contract, not the dependent service's native env
Originally the postgres image’s own env (POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB) was independent of the orchestrator’s SIMULATOR_DATABASE_* — setting one wouldn’t propagate to the other. Silent-auth footgun. Fix: alias the dependent service’s env to the orchestrator’s:
Plus a pg_isready healthcheck so the dependent service waits on the credential the orchestrator will actually use. One .env override controls both.
Generalize: whenever two services share a default DB credential (or any default), route the dependent service’s native env through the orchestrator’s contract names. Otherwise you ship a footgun where one override silently desynchronizes from the other.
Audit-Driven Polish Patterns Worth Keeping
Derive UI summary strings from the actual invocation-args function — never hardcode the verb + flags in summary text. Pre-fix bug: success-row literal said "up -d --build --remove-orphans" while up_compose_args omitted --build for sim → silent drift. Fix: derive both call and summary from one source (up_compose_args output).
Single-source the image tag and orchestrator service name — SIM_IMAGE_TAG = "dev", SIM_ORCHESTRATOR_SERVICE = "mando-simulator". Templates use placeholders (PH_SIM_IMAGE_TAG = "__MANDO_SIM_IMAGE_TAG__", PH_SIMULATOR_SERVICE = "__MANDO_SIMULATOR_SERVICE__") substituted by the renderer in templates.rs:354-355. Bare literals like mando-simulator:dev in the YAML template are duplication — kill them.
volume.rs error string uses the project-name consts (COMPOSE_PROJECT_NAME / SIMULATOR_PROJECT_NAME) — same single-source rule applied to operator-facing messages.
-b/--build help text reworded: “no-op on dev, primarily for sim” — flag behavior diverges across the two stacks; the help string says so.
MANDO_SIMULATOR_IMAGE-without-pull Warn row added to the simulator plan render — surfaces the case where the user pinned an image but didn’t pull it.
Commit Metadata
Commit: c3f0af7 on feature/simulator-runtime
Author: andras.lederer <andras.lederer@alpiq.com> (no Co-Authored-By trailer)