feat(merge_configs): expose public merge_configs(*dicts) API per §3.1 deep-merge algorithm #11
Closed
opened 2026-06-03 05:58:51 +00:00 by hurui200320
·
6 comments
No Branch/Tag Specified
Labels
Clear labels
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
hurui200320
Notifications
Due Date
No due date set.
Blocks
Depends on
#17 feat(public-api): expose all router-facing APIs at cleveractors package level; update README
cleveragents/cleveractors-core
You do not have permission to read 1 dependency
#19 feat(merge_configs): expose public merge_configs(*dicts) API per §3.1 deep-merge algorithm
cleveragents/cleveractors-core
#21 chore(merge_configs): remove bot-introduced duplicate from runtime.py and restore canonical import
cleveragents/cleveractors-core
Reference: cleveragents/cleveractors-core#11
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
Only an internal
_merge_configs(base, new)instance method exists onReactiveConfigParser. It is not publicly importable and mutates its input. The CleverThis router needs a publicmerge_configs()function to combine a platform base config with the actor config stored in the database (e.g., injecting default system prompts, org-level LLM settings, or per-tenant overrides).Spec references: ADR-2024 (Router-Facing API), Actor Configuration Standard §3.1
What Is Currently Missing
merge_configsfunction exists._merge_configsmutatesbasein place.Acceptance Criteria
Implement
merge_configs(*dicts: dict[str, Any]) -> dict[str, Any]and export it fromcleveractors/__init__.py:{}.cleveractors/__init__.pyand listed in__all__.Subtasks
merge_configs(*dicts)as a module-level function{}cleveractors/__init__.pyand__all__Definition of Done
from cleveractors import merge_configsworks without error.Implementation Notes & Inferred Metadata
Inferred Metadata (missing from issue body)
The issue body lacks a
## Metadatasection as required by CONTRIBUTING.md. The following values have been inferred and are recorded here for traceability:feat(merge_configs): expose public merge_configs(*dicts) API per §3.1 deep-merge algorithm(taken verbatim from the issue title, which is in Conventional Changelog format)
feature/merge-configs-apiReference
Spec Reference — §3.1 Deep-Merge Algorithm
Sourced from
docs/actor-standard.mdincleveragents/cleveragents-webapp@develop:Design Decisions
Module placement:
merge_configs()will be implemented as a module-level function in a new filesrc/cleveractors/config_utils.py. This follows the Single Responsibility Principle and keepsreactive/config_parser.pyfocused on parsing.Immutability: Unlike the existing
_merge_configsinstance method (which mutatesbasein place), the publicmerge_configs()function will usecopy.deepcopy()to build a new accumulator at each step, ensuring input dicts are never modified.Variadic design: Instead of
merge_configs(base, new), the public API accepts*dictsto allow merging an arbitrary number of configs in a single call (including zero args → returns{}).Existing
_merge_configsunchanged: The internal method onReactiveConfigParserwill remain as-is to avoid breaking the existing parse path. The new public function is independent.Export:
merge_configswill be added tocleveractors/__init__.pyand__all__so consumers can dofrom cleveractors import merge_configs.Test Plan (BDD scenarios)
Feature file:
features/merge_configs.featureSteps file:
features/steps/merge_configs_steps.pyScenarios:
{}Implementation locations
src/cleveractors/config_utils.py— new module withmerge_configs()functionsrc/cleveractors/__init__.py— add import and__all__entryfeatures/merge_configs.feature— BDD scenariosfeatures/steps/merge_configs_steps.py— step definitionsCHANGELOG.md— new entry for this featureImplementation Complete — PR Submitted
All 6 subtasks are checked off. Pull request #19 has been submitted for review.
Files Changed
src/cleveractors/config_utils.pymerge_configs()and_apply_merge()src/cleveractors/__init__.pymerge_configsimport and__all__entryfeatures/merge_configs.featurefeatures/steps/merge_configs_steps.pyrobot/config.robotrobot/CleverActorsLib.pybenchmarks/merge_configs_benchmark.pyCHANGELOG.md[Unreleased] > AddedQuality Gate Summary
(1 pre-existing warning for optional
langchain_google_genaidep — not introduced by this PR)config_utils.pyat 100%Verification
PR: #19
Self-QA Implementation Notes (Cycles 1–2)
Cycle 1
Review findings (0C / 3M / 5m / 3n):
Major:
merge_configscrashed withAttributeErrorwhen aNoneargument was passed —_apply_mergecalledNone.items()instead of failing with a clearTypeError.CONTRIBUTORS.md— CONTRIBUTING.md rule 8 requires updating this file; it did not exist in the repository.Minor:
4. BDD single-dict scenario only asserted top-level object identity, not deep-copy semantics (a shallow copy would also pass).
5. Mutation-guard scenario did not verify result independence from inputs (mutating result could affect inputs without detection).
6. Robot
merge_configs_two_dictskeyword lackedlen(result) == 2assertion and overlay immutability check.7. No runtime argument validation at the public API boundary (subsumed by Major #1).
8. Issue #11 body lacks required
## Metadataand## Definition of Donesections per CONTRIBUTING.md.Nits:
9. Dead
copy.deepcopyassignments in step definitions (context.input_dict,context.original_base,context.original_new) — stored but never asserted against.10. Three-way merge benchmark used unrealistically small fixtures (2–3 keys each).
11. Missing BDD scenario for empty dict as an argument.
Fixes applied:
isinstance(source, dict)guard inmerge_configsloop (src/cleveractors/config_utils.py). RaisesTypeErrorwith descriptive message before_apply_mergeis called.CONTRIBUTORS.mdat repo root with entry:Rui Hu <rui.hu@cleverthis.com> (@hurui200320).{"a": 1, "b": {"x": 2}}and added step assertingresult["b"] is not context.single_dict["b"](deep-copy identity check).result["shared"]["value"]then asserts both original inputs are unchanged.assert len(result) == 2andassert overlay == {overlay_key: overlay_val}tomerge_configs_two_dictsRobot keyword inCleverActorsLib.py.copy.deepcopyassignments and unusedimport copyfrom step definitions.Noneargument raisesTypeError.Deferred:
cleveractors-core. This is a repository-level process gap — noted in PR description. Must be resolved before merge per CONTRIBUTING.md rule 11.## Metadataand## Definition of Donesections are absent from the issue body. Values are available in the existing implementation notes comment. Should be added to the issue body directly.Cycle 2
Review findings (0C / 0M / 6m / 5n):
Minor:
import sysunused inrobot/CleverActorsLib.py(pre-existing, but file was modified by this PR).Noneas first argument raisesTypeError(only second-argNonewas tested).list → dict,dict → list).merge_configs()directly with inline dicts — not exercising the config-loading pipeline (systemic issue, not unique to this PR).existing.extend(copy.deepcopy(new_value))would be O(k·m).Nits:
N1.
CONTRIBUTORS.mdmissing trailing newline.N2.
benchmarks/merge_configs_benchmark.pyteardownmethod has no executable body (docstring only).N3. Docstring style inconsistency (NumPy-style vs. plain/minimal in rest of codebase).
N4. Missing stress benchmarks (1 000+ keys, 20+ nesting levels, large list appends).
N5. Hardcoded mutation path in step definition (
context.result["shared"]["value"]) — fragile coupling to scenario data.Verdict: Approve — No major or critical issues. All §3.1 deep-merge rules correctly implemented, immutability rigorously maintained, public API properly exported. Minor issues are test coverage gaps and a small performance improvement opportunity; none affect correctness.
Fixes applied in Cycle 2: None — verdict was Approve. Minor/nit issues are noted for future improvement but do not block merge.
Remaining Issues
The following items remain open after both cycles:
cleveractors-core; PR and issue cannot be assigned## Metadataand## Definition of Donesectionsimport sysunused inCleverActorsLib.pyNoneas first argumentextendwould be O(k·m)CONTRIBUTORS.mdmissing trailing newlineteardownmethod has docstring-only bodySelf-QA Implementation Notes (Cycles 1–2)
Cycle 1
Review findings (0C / 4M / 5m / 4n):
Closes #11textual reference only; no machine-readable Forgejo dependency link existed.Noneas first argument. TypeError scenario only testedNoneas second argument; first-position guard was untested.RecursionError). Needs documentation.scalar→listandscalar→dictpaths untested.import sysinrobot/CleverActorsLib.py(file was modified by this PR).config_utils.pyused NumPy-style docstrings; rest of codebase uses plain/minimal style.merge_configs()directly with inline dicts, not exercising the config-loading pipeline.teardownmethod in benchmarks (docstring-only body, nopass).context.result["shared"]["value"]).deepcopysecurity note absent from public API docstring.Fixes applied:
v2.1.0(ID 135) incleveractors-coreand assigned to both Issue #11 and PR #19.Scenario: Lists inside nested dicts are appendedtofeatures/merge_configs.feature, testingmerge_configs({"outer": {"items": [1, 2]}}, {"outer": {"items": [3, 4]}}) == {"outer": {"items": [1, 2, 3, 4]}}.None as first argument raises TypeErrorandNone as only argument raises TypeError— with corresponding step definitions infeatures/steps/merge_configs_steps.py... warning::block inconfig_utils.pymodule docstring documenting that circular references cause infinite loops.Scalar replaced by listandScalar replaced by dictscenarios tofeatures/merge_configs.feature.import sysfromrobot/CleverActorsLib.py.config_utils.pyfrom NumPy-style to plain/minimal docstrings matching the rest ofsrc/cleveractors/. Retained.. note::and.. warning::rST directives for security and circular reference documentation.Merge Configs Through Config Pipelinetest case inrobot/config.robotandmerge_configs_through_config_pipelinekeyword inrobot/CleverActorsLib.py. Loads YAML throughConfigurationManager.load_files(), callsto_dict(), then merges an overlay viamerge_configs().teardownmethod frombenchmarks/merge_configs_benchmark.py.step_mutate_nested_value_in_resultto dynamically discover a mutable leaf using a sentinel pattern instead of hardcodingcontext.result["shared"]["value"].merge_configs()docstring... note::block warning aboutdeepcopyexecuting arbitrary code from untrusted objects.Quality gates after Cycle 1 fixes:
nox -e lintnox -e typechecknox -e unit_testsnox -e integration_testsnox -e coverage_reportCycle 2
Review findings: Approved — no critical or major issues found.
Minor/nit observations noted for awareness (not blocking):
merge_configs_two_dictskeyword only tests distinct keys, never key override (BDD suite covers this thoroughly).Anyused without import inrobot/CleverActorsLib.pyline 142 (local variable annotation; no runtime error but static analysis gap).step_mutate_nested_value_in_resultonly searches two levels deep (robustness concern for future scenarios).list→dictanddict→listtype mismatches.pyrightconfig.jsonoverridespyproject.tomlstrict mode (typecheck runs in off mode).COVERAGE_THRESHOLD = 96.5conflicts with documented 97% minimum in CONTRIBUTING.md.Fixes applied: None — verdict was Approve. The minor/nit items above are noted for follow-up in a separate ticket if desired.
Remaining Issues
The following pre-existing issues were identified but are out of scope for this PR:
pyrightconfig.jsonoverrides strict mode —typeCheckingMode: "off"inpyrightconfig.jsontakes precedence overpyproject.tomlstrict configuration. Should be fixed in a separate maintenance ticket.COVERAGE_THRESHOLD = 96.5vs documented 97% — The noxfile threshold is lower than the CONTRIBUTING.md standard. Should be aligned in a separate maintenance ticket.## Metadataand## Definition of Donesections per CONTRIBUTING.md. Should be updated before closing.ℹ️ Bot Interference — Duplicate
merge_configsinruntime.pyThis ticket is already closed ✅ and properly implemented in commit
6b80be2. However, the bot's subsequent commite7a7d39(pushed directly tomasteron 2026-06-04) introduced a duplicatemerge_configsfunction insidesrc/cleveractors/runtime.py, and re-exports it from__init__.py.What the bot did
In
runtime.py, the bot defined its ownmerge_configs+_deep_merge_twohelper:In
__init__.py, the bot aliased the canonical version as private and re-exported the duplicate:Problems
merge_configsfromconfig_utilsis now hidden behind a_legacy_merge_configsalias, making it look like it's deprecated — it is not. It is the correct, tested implementation.__all__list now exports the bot's duplicate instead of the canonical one.What needs to be cleaned up
merge_configsand_deep_merge_twofromruntime.pyentirely.__init__.pyimport to use the canonical function directly:merge_configsremains in__all__(it already was — this is just restoring the correct source).This clean-up should be done as part of the next commit to
master(e.g., bundled with thefeature/validate-dict-apimerge, or as a standalone chore commit).Implementation Note — chore/remove-duplicate-merge-configs
What Was Done
This fix removes the bot-introduced duplicate
merge_configsimplementation that was added in commite7a7d39and restores the canonical import wiring.Files Modified
src/cleveractors/runtime.py# Config mergingsection:merge_configs(*dicts)function and_deep_merge_two(base, override)helper are gone.merge_configsentry.ruffB007 lint violation in_build_factory_config: renamed unused loop variableagent_name→_agent_name.src/cleveractors/__init__.pyfrom cleveractors.config_utils import merge_configs as _legacy_merge_configs→from cleveractors.config_utils import merge_configs(canonical, no alias).merge_configsfrom thefrom cleveractors.runtime import (...)block.merge_configsremains in__all__— now correctly backed byconfig_utils.merge_configs.src/cleveractors/config_utils.py— not touched. The canonical implementation is correct.Quality Gates
Quality gates were run locally prior to commit (lint, typecheck, unit_tests, integration_tests all passed). The pipeline will confirm on the PR.
PR
#21