Skip to content

refactor(core)!: trim the plugin protocol and standardize on register_handlers (#487 part 2, #451) - #569

Merged
mlieberman85 merged 4 commits into
darnitdevorg:mainfrom
mlieberman85:487-trim-plugin-protocol
Oct 8, 2026
Merged

mlieberman85 merged 4 commits into
darnitdevorg:mainfrom
mlieberman85:487-trim-plugin-protocol

Conversation

@mlieberman85

Copy link
Copy Markdown
Contributor

Summary

Part 2 of #487, plus #451. Both change the same plugin classes, the darnit-hello template and the plugin-author docs, so they're in one PR as separate commits. Spec first.

#487: trim ComplianceImplementation. Five protocol methods had no production caller:

  • get_all_controls and get_controls_by_level: only called by the implementations themselves;
  • get_rules_catalog and get_remediation_registry: no callers;
  • register_controls: called behind hasattr, and a no-op in every implementation.

Controls, SARIF rules and remediations come from the framework TOML. This PR removes the methods from:

  • the Protocol;
  • all six in-tree plugins (baseline, csl, example, gittuf, hello, reproducibility);
  • the three hasattr call sites;
  • the unguarded calls in gittuf, reproducibility and example.

The Protocol is @runtime_checkable and discovery checks it with isinstance, so dropping members only loosens that check. A third-party plugin that still defines the old methods is still discovered. A new test covers both a plugin with the legacy methods and one without them. The protocol is now name, display_name, version, spec_version and get_framework_config_path, plus the optional register_handlers().

#451: one handler-registration hook.

  • register_handlers() is the protocol hook. gittuf and reproducibility rename register_sieve_handlers, and darnit-example drops its duplicate. Their register() functions no longer register handlers as a side effect of discovery; the framework calls the hook itself.
  • darnit-csl used to register at module import. It now uses register_handlers(), so a registry reset no longer loses its step type (new test).
  • darnit-hello gets a documented no-op register_handlers() to show the shape.
  • Compatibility: core/discovery.py still calls register_sieve_handlers for out-of-tree plugins, with a comment that it's kept for compatibility and isn't a supported choice. A new test checks that a plugin with only register_sieve_handlers still registers.

Spec:

  • framework-design.md §6 has the trimmed protocol, and §6.4 names the hook. Appendix C gets a "Removed" entry. Version 1.0.0-alpha.16.
  • CLAUDE.md and the plugin-author guides are updated to match.
  • The CHANGELOG lists each removed method as BREAKING, for code that called these methods on a discovered implementation.

Closes #451
Refs #487

Type of Change

  • Bug fix (non-breaking change fixing an issue)
  • New feature (non-breaking change adding functionality)
  • Breaking change (fix or feature causing existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)

Framework Changes Checklist

  • Updated framework spec (docs/architecture/framework-design.md) if behavior changed
  • Ran uv run python scripts/validate_sync.py --verbose and it passes

Testing

  • Tests pass locally (uv run pytest tests/ -v): 5150 passed, 26 skipped (main: 5148)
  • Added tests for new functionality (if applicable)
  • Linting passes (uv run ruff check .)

Also run:

  • integration tests with GitHub Actions environment variables set: 334 passed;
  • darnit audit . with -f reproducibility, -f gittuf and -f community-spec: each plugin's own step types run, and none is reported as unregistered;
  • one MCP server per framework (reproducibility, gittuf, community-spec), each built in a fresh process with create_server: each registers its plugin's step types;
  • the plugin_discovery_smoke checks from CI, run locally against darnit-hello: pass.

AI assistance

  • No AI assistance was used
  • AI assistance was used

Claude (Claude Code, claude-opus-5-5) made this change: spec, code, tests and docs. This description was also drafted with Claude. Commits carry an Assisted-by: Claude:claude-opus-5-5 trailer.

Additional Notes

Left for later:

  • server/factory.py has its own copy of the handler-registration helper, which calls only register_handlers. Audits register through register_implementation_handlers, so this has no effect in practice, but the factory could delegate to it.
  • The core/plugin.py comment block lists get_check_handlers, get_context_handlers and get_remediation_handlers, which nothing calls.
  • darnit-hello and some guide snippets still use Path(__file__) rather than importlib.resources (feature 021). The CLAUDE.md example now uses importlib.resources.
  • Part 3 of Remove dead adapter system, legacy storage backend, unused protocol methods, and other unreachable code (~3,500 lines) #487 (fold darnit-example into darnit-testchecks) is next.

🤖 Generated with Claude Code

…e handler hook (darnitdevorg#487, darnitdevorg#451)

ComplianceImplementation keeps only name, display_name, version,
spec_version and get_framework_config_path. get_all_controls,
get_controls_by_level, get_rules_catalog, get_remediation_registry and
register_controls move to Appendix C: no production path called the
first four, and register_controls was required to be a no-op.

Section 6.4 names register_handlers() as the handler registration hook,
accepts register_sieve_handlers() only for compatibility, and notes that
import-time registration works but cannot be introspected. Section 12
points at it. Version 1.0.0-alpha.16.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Michael Lieberman <mlieberman85@gmail.com>
…arnitdevorg#487)

BREAKING CHANGE: ComplianceImplementation no longer declares
get_all_controls, get_controls_by_level, get_rules_catalog,
get_remediation_registry or register_controls, and the framework no
longer calls register_controls(). Controls, SARIF rules and remediations
come from the framework TOML. Plugins that still define the methods are
discovered as before; the methods are not called.

Removes the methods from every in-tree implementation (baseline, csl,
example, gittuf, hello, reproducibility) together with the Protocol,
since discovery checks isinstance() against it. darnit-example also
loses the _RULES catalog, remediation/registry.py and the empty
controls package that only fed them.

Implementation tests that read controls through get_all_controls now
load the framework TOML with load_controls_from_framework. New tests
show a plugin with and without the removed methods both pass discovery,
and one without get_framework_config_path does not.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Michael Lieberman <mlieberman85@gmail.com>
…plugin (darnitdevorg#451)

register_handlers() is the one handler hook (framework-design 6.4):

- darnit-gittuf and darnit-reproducibility rename register_sieve_handlers
  to register_handlers, and their register() entry points no longer
  register handlers as a side effect of discovery.
- darnit-example folds its step types into register_handlers.
- darnit-csl stops registering csl_llm_if_present at package import and
  registers it from register_handlers; register_implementation_handlers
  and the strict loader already call the hook on every audit and load.
- darnit-hello gains register_handlers as a documented no-op.

discovery still calls register_sieve_handlers, now documented as a
compatibility shim for out-of-tree plugins. Tests cover a plugin with
only the old name, the csl step type after a registry reset, and that
every in-tree plugin defines only register_handlers.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Michael Lieberman <mlieberman85@gmail.com>
…col (darnitdevorg#487, darnitdevorg#451)

CHANGELOG: BREAKING bullets for the removed ComplianceImplementation
methods (Removed) and for register_handlers() as the one handler hook
(Changed).

CLAUDE.md, ARCHITECTURE.md, the darnit README, IMPLEMENTATION_GUIDE,
packaging-plugins, HANDLER_AUTHORING, the getting-started pages, the
tutorial, the example-plugin spec and the reproducibility design sketch
now show the five-member protocol and register sieve step types in
register_handlers(), with register_sieve_handlers() described as a
compatibility shim and import-time registration as not introspectable.
IMPLEMENTATION_GUIDE section 7 no longer describes the Python
REMEDIATION_REGISTRY that only get_remediation_registry() read.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Michael Lieberman <mlieberman85@gmail.com>
@mlieberman85
mlieberman85 merged commit f606dbd into darnitdevorg:main Oct 8, 2026
8 checks passed
@mlieberman85
mlieberman85 deleted the 487-trim-plugin-protocol branch October 8, 2026 23:55
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.

Standardize on one sieve-handler registration method across plugins

1 participant