hub-core/hub_core/runtime/validation.py

71 lines
3.2 KiB
Python
Raw Normal View History

from __future__ import annotations
import json
from typing import Any
from jsonschema import Draft202012Validator, FormatChecker
Close HUB-WP-0009 conformance gaps (C2, C7, C9, C10); mark blocked workplans Implements the four residual conformance checks left open by the T04 minimal vertical: - C2: GET /ports/registry/registrations/{hub_slug} resolves missing (404), ambiguous (shared reuse_surface_id across hub_slugs), and stale (deprecated/retired descriptor) registrations; a new .../audit route exposes queryable registration history from the existing in-memory history and the PostgreSQL runtime_audit_ledger. - C7: harness proof that disabled compatibility groups deny access (404) with no fixture credentials involved, matching the existing fail-closed compat router behavior. - C9: harness proof plus a dedicated test that /readyz degrades only on an unavailable configured dependency while unrelated disabled projections stay non-blocking. - C10: ContractValidator now negotiates contract_version_min/max against the runtime's contract version and rejects incompatible or inverted ranges with an explicit 422 instead of silently accepting them. HUB-WP-0009 is now finished. HUB-WP-0006 is marked blocked: its only open task (T06) has no remaining hub-core code path and waits on an external Forgejo identity/production deployment gate. HUB-WP-0011 is marked blocked: T02/T03 already waited on external credential/deployment review, and T01 needs a source/destination ownership and retention decision against live message data before it can be implemented safely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 310936@bnt-lap001 Assistant-Session: 00cd9abe-09a0-416b-88e0-f907b9101629
2026-09-27 23:59:35 +02:00
from hub_core.contracts import CONTRACT_VERSION, extension_contract_root
from hub_core.runtime.models import RegistryRegistration
class ContractValidator:
"""Validate runtime registration input against the packaged contract."""
Close HUB-WP-0009 conformance gaps (C2, C7, C9, C10); mark blocked workplans Implements the four residual conformance checks left open by the T04 minimal vertical: - C2: GET /ports/registry/registrations/{hub_slug} resolves missing (404), ambiguous (shared reuse_surface_id across hub_slugs), and stale (deprecated/retired descriptor) registrations; a new .../audit route exposes queryable registration history from the existing in-memory history and the PostgreSQL runtime_audit_ledger. - C7: harness proof that disabled compatibility groups deny access (404) with no fixture credentials involved, matching the existing fail-closed compat router behavior. - C9: harness proof plus a dedicated test that /readyz degrades only on an unavailable configured dependency while unrelated disabled projections stay non-blocking. - C10: ContractValidator now negotiates contract_version_min/max against the runtime's contract version and rejects incompatible or inverted ranges with an explicit 422 instead of silently accepting them. HUB-WP-0009 is now finished. HUB-WP-0006 is marked blocked: its only open task (T06) has no remaining hub-core code path and waits on an external Forgejo identity/production deployment gate. HUB-WP-0011 is marked blocked: T02/T03 already waited on external credential/deployment review, and T01 needs a source/destination ownership and retention decision against live message data before it can be implemented safely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 310936@bnt-lap001 Assistant-Session: 00cd9abe-09a0-416b-88e0-f907b9101629
2026-09-27 23:59:35 +02:00
def __init__(self, *, runtime_contract_version: str = CONTRACT_VERSION) -> None:
self._runtime_contract_version = _parse_semver(runtime_contract_version)
contract_root = extension_contract_root()
schema_root = contract_root.joinpath("schemas")
self._descriptor = _validator(schema_root.joinpath("hub-descriptor.schema.json"))
self._manifest = _validator(schema_root.joinpath("hub-manifest.schema.json"))
catalog = json.loads(
contract_root.joinpath("catalogs", "event-types.json").read_text(encoding="utf-8")
)
self._event_families = {
entry["type"]: entry["family"] for entry in catalog["event_types"]
}
def validate_registration(self, registration: RegistryRegistration) -> None:
self._descriptor.validate(registration.descriptor)
self._manifest.validate(registration.manifest)
descriptor_id = registration.descriptor.get("reuse_surface_id")
manifest_id = registration.manifest.get("reuse_surface_id")
if descriptor_id != manifest_id:
raise ValueError("descriptor and manifest reuse_surface_id must match")
Close HUB-WP-0009 conformance gaps (C2, C7, C9, C10); mark blocked workplans Implements the four residual conformance checks left open by the T04 minimal vertical: - C2: GET /ports/registry/registrations/{hub_slug} resolves missing (404), ambiguous (shared reuse_surface_id across hub_slugs), and stale (deprecated/retired descriptor) registrations; a new .../audit route exposes queryable registration history from the existing in-memory history and the PostgreSQL runtime_audit_ledger. - C7: harness proof that disabled compatibility groups deny access (404) with no fixture credentials involved, matching the existing fail-closed compat router behavior. - C9: harness proof plus a dedicated test that /readyz degrades only on an unavailable configured dependency while unrelated disabled projections stay non-blocking. - C10: ContractValidator now negotiates contract_version_min/max against the runtime's contract version and rejects incompatible or inverted ranges with an explicit 422 instead of silently accepting them. HUB-WP-0009 is now finished. HUB-WP-0006 is marked blocked: its only open task (T06) has no remaining hub-core code path and waits on an external Forgejo identity/production deployment gate. HUB-WP-0011 is marked blocked: T02/T03 already waited on external credential/deployment review, and T01 needs a source/destination ownership and retention decision against live message data before it can be implemented safely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 310936@bnt-lap001 Assistant-Session: 00cd9abe-09a0-416b-88e0-f907b9101629
2026-09-27 23:59:35 +02:00
self._negotiate_contract_version(registration.descriptor)
def _negotiate_contract_version(self, descriptor: dict[str, Any]) -> None:
version_min = _parse_semver(descriptor["contract_version_min"])
version_max = _parse_semver(descriptor["contract_version_max"])
if version_min > version_max:
raise ValueError(
"descriptor contract_version_min must not exceed contract_version_max"
)
if not (version_min <= self._runtime_contract_version <= version_max):
raise ValueError(
"descriptor requires contract version range "
f"{descriptor['contract_version_min']}-{descriptor['contract_version_max']}, "
f"incompatible with runtime contract version {CONTRACT_VERSION}"
)
def validate_event_family(self, event_type: str, expected_family: str) -> None:
actual_family = self._event_families.get(event_type)
if actual_family is None:
raise ValueError(f"event type '{event_type}' is not cataloged")
if actual_family != expected_family:
raise ValueError(
f"event type '{event_type}' belongs to '{actual_family}', not '{expected_family}'"
)
def _validator(resource: Any) -> Draft202012Validator:
schema = json.loads(resource.read_text(encoding="utf-8"))
Draft202012Validator.check_schema(schema)
return Draft202012Validator(schema, format_checker=FormatChecker())
Close HUB-WP-0009 conformance gaps (C2, C7, C9, C10); mark blocked workplans Implements the four residual conformance checks left open by the T04 minimal vertical: - C2: GET /ports/registry/registrations/{hub_slug} resolves missing (404), ambiguous (shared reuse_surface_id across hub_slugs), and stale (deprecated/retired descriptor) registrations; a new .../audit route exposes queryable registration history from the existing in-memory history and the PostgreSQL runtime_audit_ledger. - C7: harness proof that disabled compatibility groups deny access (404) with no fixture credentials involved, matching the existing fail-closed compat router behavior. - C9: harness proof plus a dedicated test that /readyz degrades only on an unavailable configured dependency while unrelated disabled projections stay non-blocking. - C10: ContractValidator now negotiates contract_version_min/max against the runtime's contract version and rejects incompatible or inverted ranges with an explicit 422 instead of silently accepting them. HUB-WP-0009 is now finished. HUB-WP-0006 is marked blocked: its only open task (T06) has no remaining hub-core code path and waits on an external Forgejo identity/production deployment gate. HUB-WP-0011 is marked blocked: T02/T03 already waited on external credential/deployment review, and T01 needs a source/destination ownership and retention decision against live message data before it can be implemented safely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 310936@bnt-lap001 Assistant-Session: 00cd9abe-09a0-416b-88e0-f907b9101629
2026-09-27 23:59:35 +02:00
def _parse_semver(value: str) -> tuple[int, int, int]:
core = value.split("+", 1)[0].split("-", 1)[0]
major, minor, patch = core.split(".")
return (int(major), int(minor), int(patch))