Domain: layered configuration merging library

Issue:
When a configuration source overrides one nested setting, every sibling setting in the same section is lost. For example, loading with a config file containing only a database pool size causes the database host, port, and pool timeout to disappear from the effective configuration instead of keeping their defaults. The same happens with environment-variable overrides. Expected behavior: overrides apply on top of the defaults, so settings not mentioned by an override keep their existing values at every nesting level.

Repository files:
content: # Present so pytest adds the repository root to sys.path, making `import src`
# resolve from the test suite.

path: conftest.py

content: # Pure standard library unless the environment image pre-bakes wheels.

path: requirements.lock

path: src/__init__.py

content: """Public API: load the effective configuration."""

from src.defaults import DEFAULTS
from src.loader import from_env, from_file
from src.merge import merge_layers


def load_config(file_overrides=None, environ=None):
    """Compose the effective configuration.

    Layers, lowest precedence first: built-in defaults, then the parsed
    config file, then prefixed environment variables.
    """
    config = merge_layers(DEFAULTS, from_file(file_overrides))
    config = merge_layers(config, from_env(environ or {}))
    return config

path: src/config.py

content: """Built-in default configuration tree."""

DEFAULTS = {
    "app_name": "service",
    "db": {
        "host": "localhost",
        "port": 5432,
        "pool": {"size": 5, "timeout_seconds": 30},
    },
    "logging": {"level": "INFO", "json": False},
    "retries": {"attempts": 3, "backoff_seconds": 2},
}

path: src/defaults.py

content: """Override sources: parsed config files and environment variables."""

_ENV_PREFIX = "APP_"


def from_file(data):
    """A parsed config file is already a nested mapping; validate shallowly."""
    if data is None:
        return {}
    if not isinstance(data, dict):
        raise TypeError("config file must parse to a mapping")
    return data


def _coerce(raw):
    lowered = raw.lower()
    if lowered in ("true", "false"):
        return lowered == "true"
    try:
        return int(raw)
    except ValueError:
        return raw


def from_env(environ):
    """Build a nested override tree from prefixed environment variables.

    ``APP_DB__POOL__SIZE=10`` becomes ``{"db": {"pool": {"size": 10}}}``;
    double underscores separate nesting levels and values are coerced to
    bool/int where possible.
    """
    tree = {}
    for key, raw in environ.items():
        if not key.startswith(_ENV_PREFIX):
            continue
        path = key[len(_ENV_PREFIX):].lower().split("__")
        node = tree
        for part in path[:-1]:
            node = node.setdefault(part, {})
        node[path[-1]] = _coerce(raw)
    return tree

path: src/loader.py

content: """Layer merging for configuration trees."""


def merge_layers(base, override):
    """Return a new dict with ``override`` applied on top of ``base``.

    Later layers win: a key present in ``override`` takes precedence over
    the same key in ``base``.
    """
    result = dict(base)
    for key, value in override.items():
        result[key] = value
    return result

path: src/merge.py

content: """Tests for the configuration loader."""

from src.config import load_config
from src.loader import from_env


def test_defaults_when_no_overrides():
    config = load_config()
    assert config["db"]["host"] == "localhost"
    assert config["db"]["pool"]["size"] == 5
    assert config["logging"]["level"] == "INFO"


def test_top_level_scalar_override():
    config = load_config(file_overrides={"app_name": "billing"})
    assert config["app_name"] == "billing"
    assert config["retries"]["attempts"] == 3


def test_full_section_override():
    config = load_config(file_overrides={"logging": {"level": "DEBUG", "json": True}})
    assert config["logging"] == {"level": "DEBUG", "json": True}


def test_env_parsing_builds_nested_tree():
    tree = from_env({"APP_DB__POOL__SIZE": "10", "APP_LOGGING__JSON": "true", "IGNORED": "x"})
    assert tree == {"db": {"pool": {"size": 10}}, "logging": {"json": True}}

path: tests/public/test_public.py

Reference patch:
--- a/src/merge.py
+++ b/src/merge.py
@@ -9,5 +9,8 @@
     """
     result = dict(base)
     for key, value in override.items():
-        result[key] = value
+        if isinstance(value, dict) and isinstance(result.get(key), dict):
+            result[key] = merge_layers(result[key], value)
+        else:
+            result[key] = value
     return result


Hidden acceptance tests (diff):
diff --git a/tests/hidden/test_hidden.py b/tests/hidden/test_hidden.py
new file mode 100644
--- /dev/null
+++ b/tests/hidden/test_hidden.py
@@ -0,0 +1,33 @@
+"""Hidden evaluation tests. Never agent-visible (DEC-015).
+
+FAIL_TO_PASS: on the buggy fixture, a partial nested override replaces the
+whole section (shallow merge), so sibling settings vanish and all three
+tests fail. A correct recursive merge passes all three.
+"""
+
+from src.config import load_config
+
+
+def test_partial_file_override_preserves_siblings():
+    config = load_config(file_overrides={"db": {"pool": {"size": 10}}})
+    assert config["db"]["pool"]["size"] == 10
+    assert config["db"]["pool"]["timeout_seconds"] == 30
+    assert config["db"]["host"] == "localhost"
+    assert config["db"]["port"] == 5432
+
+
+def test_env_override_merges_into_defaults():
+    config = load_config(environ={"APP_DB__POOL__TIMEOUT_SECONDS": "60"})
+    assert config["db"]["pool"]["timeout_seconds"] == 60
+    assert config["db"]["pool"]["size"] == 5
+    assert config["db"]["host"] == "localhost"
+
+
+def test_file_then_env_layers_accumulate():
+    config = load_config(
+        file_overrides={"db": {"port": 6432}},
+        environ={"APP_DB__POOL__SIZE": "12"},
+    )
+    assert config["db"]["port"] == 6432
+    assert config["db"]["pool"]["size"] == 12
+    assert config["db"]["host"] == "localhost"