Menu

#200 plugins+docs: complete the doberman.detectors extension seam (example + docs/EXTENDING.md)

open
nobody
2026-08-20
2026-07-30
Anonymous
No

Originally created by: fu351
Originally owned by: snowyukitty

What

Doberman's plugin registry (src/doberman/engine/registry.py) declares 11 entry-point groups — doberman.rules, doberman.detectors, doberman.policy_sources, doberman.auth_providers, doberman.audit_sinks, doberman.drift_observers, doberman.cost_observers, doberman.algebra_adapters, doberman.adjudicators, doberman.egress_brokers, and doberman.async_challenge_backends (lines 38–68) — each with a working interface (a duck-typed protocol/shape check). Ten resolve through a discover_*() function; the eleventh, doberman.async_challenge_backends, resolves through active_async_backend() instead, and the docs page should flag that difference. Only one group, doberman.rules, has a runnable worked example (examples/plugin-guardrail/, with its own installable package + tests/test_example_rule.py::test_entry_point_is_discoverable_after_install), and there is no single doc page enumerating all 11 seams for a contributor deciding where to plug in. Complete the closest sibling seam, doberman.detectors (structurally identical to doberman.rules — both discover Guardrail-shaped objects, per discover_detectors()'s own docstring, lines 166–174): add a second example package mirroring examples/plugin-guardrail/'s shape but registered under doberman.detectors, with its own install-based discoverability test, and add docs/EXTENDING.md documenting all 11 groups (one short paragraph each: what shape is required, how it resolves, and a link to the one worked example).

Difficulty: level-6

Completing an extension seam end-to-end: the interface and registry already exist and are proven correct (discover_detectors, tests/unit/test_registry.py), and there's a worked example to mirror — the work is producing a second, real, installable example that proves the pattern generalizes beyond doberman.rules, an install-based test for it (mirroring examples/plugin-guardrail's), and the docs page that ties all 11 seams together for the next contributor.

Safety constraints (do not weaken these)

  • The new example detector must be genuinely raise-only, like ExampleRule (examples/plugin-guardrail/src/example_plugin/rules.py) — PASS or AUTH only, never BLOCK from a tutorial plugin, and it must never put a raw path, argument, or payload into its explanation.
  • docs/EXTENDING.md must state the same defensive-loading guarantee registry.py's module docstring already documents (a broken/hostile plugin is logged and skipped, never crashes core, and can only ever add risk via combine()) — do not describe or imply a stronger contract than the code actually provides.
  • Do not wire the new example into CI's default testpaths (mirrors [#194]'s constraint) — keep it self-contained under examples/, installed only by its own test/instructions.

Starting files

  • src/doberman/engine/registry.pydiscover_detectors() (line 166), DETECTOR_GROUP (line 39), the full list of 11 groups (lines 38–68) for the docs page.
  • examples/plugin-guardrail/ — the pattern to mirror (pyproject.toml entry-points block, src/example_plugin/rules.py, tests/test_example_rule.py).
  • New: examples/plugin-detector/ (package name e.g. doberman-example-plugin-detector, entry point under [project.entry-points."doberman.detectors"]).
  • New: docs/EXTENDING.md.

Acceptance criteria

  • [ ] examples/plugin-detector/ is a real, installable package registering an example under doberman.detectors (not doberman.rules).
  • [ ] Its own test proves real (non-monkeypatched) discovery, mirroring plugin-guardrail's test_entry_point_is_discoverable_after_install.
  • [ ] docs/EXTENDING.md documents all 11 entry-point groups from registry.py, each with: required shape, how it resolves (discover_*(), or active_async_backend() for doberman.async_challenge_backends), and a link to the one worked example (rules or detectors).
  • [ ] Neither new example is added to CI's default testpaths.
  • [ ] Existing behaviour unchanged (no test weakened, no output format broken)
  • [ ] ruff check . && ruff format --check . and lint-imports pass
  • [ ] pytest passes

How to validate

pip install -e examples/plugin-detector
pytest examples/plugin-detector/tests -v
pip uninstall -y doberman-example-plugin-detector

Out of scope

  • Do not build an example for any of the other seams in this issue — doberman.detectors only; a follow-up can cover the rest.
  • Do not modify examples/plugin-guardrail/ itself.
  • Do not add examples/ to CI's default testpaths.

Notes for contributors

New here? Start with CONTRIBUTING.md. Issues are labelled level-1 (docs only) through level-10 (new subsystem) — pick one at your level and climb. Comment to claim an issue before starting.

Related

Tickets: #102
Tickets: #132
Tickets: #184
Tickets: #194

Discussion

  • Anonymous

    Anonymous - 2026-08-14

    Originally posted by: snowyukitty

    I'd like to claim this issue. I checked current main at dd4683bc9c6a450e8edbe847833998fe8d1daa92; the registry now declares 11 entry-point groups rather than the issue's 10. The additional doberman.async_challenge_backends seam is resolved separately by active_async_backend() (with the in-memory fallback), rather than by a discover_*() function, so I would document that actual path explicitly.

    For the detector example, I plan to add a real installable doberman.detectors package with PASS/AUTH-only behavior and generic, redacted explanations. Its tests would prove real installed entry-point discovery and execution through SubjectiveGuardrail, including raise-only composition. I would also add docs/EXTENDING.md for all 11 current seams and link it from README, while leaving examples/plugin-guardrail/ and the default testpaths unchanged. If this updated scope matches your intent, I'm happy to implement it.

     
  • Anonymous

    Anonymous - 2026-08-17
     
  • Anonymous

    Anonymous - 2026-08-17

    Originally posted by: fu351

    Assigned — and good catch on the count. You're right: the registry declares 11 groups now, not 10. The new one is doberman.async_challenge_backends, and it resolves through active_async_backend() rather than a discover_*(), so it's the one seam the docs page should flag as different. I've folded the correction into the body.

    This is a close cousin of your plugin-guardrail tutorial (#132) — same shape, one seam over — so you've half-written the sister page already. docs/EXTENDING.md is the piece that ties all 11 together.

     
  • Anonymous

    Anonymous - 2026-08-20

    Originally posted by: fu351

    Hey @snowyukitty, a no-deadline check-in. The detector example plus EXTENDING.md is a real chunk of writing, so slow going is normal. If you're stuck on anything, such as how to describe the async_challenge_backends path or what the detector example should prove, ask here and I'll answer. And if life moved on since you claimed it, say the word and I'll unassign it, no hard feelings; it'll be here if you come back.

     

Log in to post a comment.