# ADR-0007: Built-in rules do not depend on registry dispatch ## Status Proposed — implemented on feat/v0.0.71 for maintainer review. ## Context The registry discovers built-in adapters, but built-in rules and GPG dispatch also imported the registry. Once scheme validation used registered plugins, the same scheme module both supplied built-in rules and consumed the registry. Lazy imports avoided startup failures but retained structural cycles reported by CodeQL. The GPG adapter also selected inner loaders from the global singleton even when registered in an independent registry. ## Decision Keep registration dependencies one-way. Built-ins accept a private structural registry interface; GPG receives its owning registry. Pure rule definitions live in `_scheme_rules.py`, which imports neither the registry nor dispatch. Rails and built-in adapters depend on those rules. The existing `schemes.py` surface re-exports the same objects and owns registry-backed validation. Preserve public import paths, signatures, shared dictionaries, qualified class names and pickle resolution. Do not change the external plugin protocols, contract version, rule behavior or generated payment XML. Regression tests enforce the dependency boundary and compatibility identities. ## Alternatives - Dismissing all cycle findings would leave the ownership problem intact. - Dynamic imports to evade the scanner would hide the same dependency cycle. - Eager initialization would undermine optional dependencies and library startup. ## Consequences There is one additional private rule module and a thin compatibility surface. Class metadata explicitly retains the established public module name. Internal callers must import rules from the private module, not through the public dispatch facade; the architecture regression test enforces this distinction.