Skip to content

feat(strategies): Base.merge for StrategyRegistry with cross-registry validation - #520

Merged
ocots merged 1 commit into
mainfrom
feat/registry-merge-517
Jul 29, 2026
Merged

feat(strategies): Base.merge for StrategyRegistry with cross-registry validation#520
ocots merged 1 commit into
mainfrom
feat/registry-merge-517

Conversation

@ocots

@ocots ocots commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds Base.merge(a::StrategyRegistry, b::StrategyRegistry...), running the equivalent of create_registry's cross-registry checks instead of a raw Dict merge, which silently let a colliding ID resolve to whichever entry won.
  • Validated invariants:
    • every strategy id() is unique across all input registries;
    • a parameter id() shared by more than one input registry resolves to the same parameter type in each;
    • no id() is used as a strategy ID in one registry and a parameter ID in another.
  • On the open design question the issue raised: when the same family is registered in more than one input, their strategy lists are unioned (still subject to the checks above), rather than rejected — this is what makes merge useful for a downstream package extending a family across two independently-built registries (e.g. a solve registry plus a flow registry), not just combining registries with disjoint families.
  • Extends Base.merge rather than introducing an exported Strategies.merge, per the issue's naming note, avoiding the shadowing problem that made downstream packages move away from @reexport.

Fixes #517

Test plan

  • New test/suite/strategies/test_registry_merge.jl (33 tests), organized as Unit / Integration / Contract / Error:
    • disjoint-family merge, 2- and 3-registry family unions of distinct strategies, the identity case
    • describe/show exercised through the merged registry, confirming a unioned family renders under a single header
    • a parameterized-strategy union and parameter id/type agreement across registries
    • every rejection path, including the key boundary: the same strategy id under a shared family across registries is still rejected, not silently merged
  • suite/strategies (1013 tests) green
  • Full suite green: 4956/4956

🤖 Generated with Claude Code

… validation

Downstream packages routinely hold two independently-built registries (e.g.
a solve registry plus CTFlows' flow registry) and want one introspection
entry point. Until now the only way to combine them was to reach into the
struct and raw-merge the internal Dicts directly, which skips every
validation create_registry enforces within one call.

Base.merge(a::StrategyRegistry, b::StrategyRegistry...) now runs the
equivalent cross-registry checks:

- every strategy id() must be unique across all input registries
- a parameter id() shared by more than one input must resolve to the same
  parameter type in each
- no id() may be used as a strategy id in one registry and a parameter id
  in another

When the same family is registered in more than one input, their strategy
lists are unioned (still subject to the checks above), which is what makes
merge useful for a downstream package extending a family across two
independently-built registries rather than only combining disjoint ones.

Extends Base.merge rather than introducing an exported Strategies.merge,
per the issue's naming note, to avoid the same shadowing problem that made
downstream packages move away from @reexport.

Fixes #517

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ocots
ocots merged commit 06a5c9f into main Jul 29, 2026
3 checks passed
@ocots
ocots deleted the feat/registry-merge-517 branch July 29, 2026 20:52
@ocots ocots mentioned this pull request Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Base.merge for StrategyRegistry, with the cross-registry validation create_registry does within one

1 participant