0031. Layered runtime configuration precedence

#Context

Runtime configuration (Trading_config.t — broker selection + credentials, server host/port, engine, watchlist, logging) is resolved from several sources at process start. The mechanism was introduced in commit 09aad21 ("config: layered configuration with JSON file + env + CLI overlays") but shipped without an ADR; the precedence contract lived only in loader.mli and the commit message. This ADR records the decision and corrects an implementation gap that violated it.

ADR 0014 governs a different concern — it makes ATD the single source of truth for cross-BC wire contracts (Commands, Integration Events, View Models) to prevent producer/consumer drift. trading_config.atd reuses the same atdgen tooling for its type, but the runtime config is not a cross-BC message; its loading precedence is out of 0014's scope and is the subject here.

#Decision

Configuration is composed from four layers, lowest to highest precedence:

default.config.json  (committed)
  │ overridden by
local.config.json    (gitignored; path via --config / TRADING_CONFIG / ./config/local.config.json)
  │ overridden by
environment variables (FINAM_ACCOUNT_ID/SECRET, BCS_CLIENT_ID/SECRET,
  │                     ALOR_PORTFOLIO/SECRET/EXCHANGE, LOG_LEVEL)
  │ overridden by
per-invocation CLI flags (--broker, --account, --secret, --exchange,
                          --port, --log-level, ...)

So: a local file overrides the committed default; environment variables override the local file; CLI flags override the environment. Defaults live in default.config.json (not in ~default ATD annotations) so changing a default is an ops edit, not a recompile.

#Broker variant vs. credential fields

The broker is a sum-type variant (Finam | Bcs | Alor | Synthetic) whose payload carries credentials. Two distinct rules apply:

#Why this needed a fix

The original implementation applied the env overlay to the file-layer config and then merged the CLI overlay on top, where Merger.merge_broker replaces the broker variant wholesale. So whenever --broker X appeared on the CLI, the sparse CLI variant (credential fields None unless --account / --secret were also passed) replaced the env-enriched broker from the lower layers, dropping the env credentials entirely. The documented "env overrides credentials" held only when the broker was not named on the CLI — and the Alor variant was never wired into the env overlay at all.

The fix resolves the broker explicitly in Loader.load, bypassing the whole-variant replacement: the variant is taken from the CLI (else the file layer), and each credential field is resolved CLI ?? env ?? file. This honours the contract for every variant, including a CLI-selected one, and covers Finam / BCS / Alor uniformly.

#Consequences

#References