Plugin interface contracts — phase 1 (advisory)¶
Companion to
docs/plugin-authoring.mdandGOVERNANCE.md. Describes the current state of theCheckResult/ProtocolPlugincontract, not an aspirational future one.
What exists today¶
uhbs_core.models.CheckResultis a plain@dataclass(id,team,passed,detail,score,evidence,critical). It is not Pydantic. There is no runtime schema validation on construction — a plugin can (and, per an architecture review, occasionally did) return aCheckResultwherepassedandscoredisagree, or ascoreoutside[0, 100].uhbs_core.protocols.base.ProtocolPluginis a plainABC. Third-party plugins registered viauhbs.pluginsentry points (seeuhbs_core.protocols.registry.load_external_plugins) are only checked withisinstance(plugin, ProtocolPlugin)— a presence/inheritance check, not a structural or type-level audit of every probe method's signature.uhbs_core.contract_validationadds an additive, opt-in advisory layer on top of the above, without changing either type:validate_check_result()/validate_module_result()— plain functions you can call by hand (or from your own test suite / CI step) against already-constructedCheckResult/ModuleResultobjects (or dict-shaped data loaded from a scorecard JSON) to catch the passed/score-disagreement, out-of-range-score, and missing-required-field bug classes an architecture review flagged. An empty list means compliant; nothing is enforced automatically.UHBSProtocolPlugin— atyping.Protocol(@runtime_checkable) documenting theprobe_fsm/probe_negotiation/probe_statemethod shape. It is satisfied structurally: every existing built-in plugin already passesisinstance(plugin, UHBSProtocolPlugin)without inheriting from it or changing a single line. Third-party plugin authors can use it purely for their own static type-checking.
Why not just migrate to Pydantic v2 now¶
A full migration of CheckResult/ModuleResult/ProtocolPlugin to
Pydantic v2 models would give real, always-on schema enforcement (coercion,
ge=0, le=100 field constraints, etc.) instead of an opt-in lint. It was
intentionally deferred for this pass because:
models.pywas being edited concurrently by another workstream in this same session (adding thecritical: boolfield used above) — a simultaneous structural rewrite of the same file is unsafe.- All 17+ built-in protocol plugins construct
CheckResultpositionally/by keyword today; a Pydantic migration touching every call site is a large, separate effort that deserves its own focused review, not a drive-by change bundled with unrelated work.
This is tracked as a follow-up in ROADMAP.md — not
done, not started as code, just written down so it isn't lost.
Honest status¶
| Claim | Reality today |
|---|---|
CheckResult/ModuleResult schema-validated on construction |
No — plain dataclasses |
ProtocolPlugin interface enforced structurally at registration |
No — isinstance (inheritance) check only |
| Advisory lint you can run by hand against check output | Yes — uhbs_core.contract_validation |
| Third-party plugins statically type-checkable against a documented shape | Yes — UHBSProtocolPlugin Protocol |
| Pydantic v2 migration of the core contract | Not started — tracked in ROADMAP.md |
See docs/architecture/ci-baseline.md for the related,
also-phase-1, golden-baseline CI note.