feat(merge_configs): expose public merge_configs(*dicts) API per §3.1 deep-merge algorithm #19

Merged
hurui200320 merged 1 commits from feature/merge-configs-api into master 2026-06-03 15:31:41 +00:00

1 Commits

Author SHA1 Message Date
hurui200320 6b80be2117 feat(merge_configs): expose public merge_configs(*dicts) API per §3.1 deep-merge algorithm
CI / lint (pull_request) Successful in 42s
CI / integration_tests (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 53s
CI / build (pull_request) Successful in 50s
CI / typecheck (pull_request) Successful in 54s
CI / security (pull_request) Successful in 1m12s
CI / unit_tests (pull_request) Successful in 3m53s
CI / coverage (pull_request) Successful in 3m50s
CI / status-check (pull_request) Successful in 3s
CI / lint (push) Successful in 43s
CI / quality (push) Successful in 46s
CI / security (push) Successful in 47s
CI / typecheck (push) Successful in 48s
CI / build (push) Successful in 50s
CI / integration_tests (push) Successful in 54s
CI / unit_tests (push) Successful in 3m49s
CI / coverage (push) Successful in 3m42s
CI / status-check (push) Successful in 3s
Implement a new module-level merge_configs() function in
src/cleveractors/config_utils.py that exposes the §3.1 deep-merge
algorithm as a public, importable API.

Motivation: The internal ReactiveConfigParser._merge_configs() method
mutates its base dict in place and only accepts two dicts. The
CleverThis router (wave 2 of cleveragents/cleveragents-webapp#275)
needs a public merge_configs() to combine platform base configs with
actor configs from the database without side-effects.

Implementation:
- merge_configs(*dicts) accumulates into a fresh dict, applying each
  overlay using the §3.1 rules:
    * Key absent → add via deep copy.
    * Both mappings → deep-merge recursively (_apply_merge helper).
    * Both sequences → extend (existing + deep copy of new).
    * Otherwise → replace with deep copy of new value.
- Runtime type guard: non-dict arguments raise TypeError immediately
  (fail-fast per CONTRIBUTING.md argument validation guidelines).
- Zero-argument call returns {}.
- Inputs are never mutated (copy.deepcopy used for all stored values).
- Internal _merge_configs on ReactiveConfigParser is unchanged.

Exports: merge_configs added to cleveractors/__init__.py import and
__all__ so consumers can use 'from cleveractors import merge_configs'.

Tests:
- 15 Behave BDD scenarios in features/merge_configs.feature covering
  zero-args, single-dict deep copy (including nested identity),
  empty-dict arguments, TypeError on None, absent-key insertion,
  scalar override, recursive mapping merge, sequence append,
  three-dict chained merge, deep nesting, input mutation guard,
  result-mutation independence from inputs, and type-mismatch
  replacement.
- 4 Robot Framework integration tests in robot/config.robot with
  strengthened assertions (len check, overlay immutability).
- 5 ASV benchmarks with realistically sized three-way merge fixtures
  (50 keys each with overlap).

Project files: Added CONTRIBUTORS.md per CONTRIBUTING.md §PR Process
rule 8.

Coverage: 97% (config_utils.py: 100%).
Quality gates: ruff lint ✓, pyright strict ✓ (0 errors),
all BDD/Robot tests ✓.

ISSUES CLOSED: #11
2026-06-03 14:44:18 +00:00