# Hub Capability Manifest Contract **Name:** hub-capability-manifest **Version:** 1.0 **Date:** 2026-03-31 **Status:** Active **Layer:** Extensions **Maturity:** Beta --- ## Purpose The Hub Capability Manifest is the formal extension registration mechanism of the IHF. It is the contract by which a domain hub (dev-hub, ops-hub, fin-hub, sec-hub) declares the vocabulary it introduces to the framework: widget types, event types, annotation categories, and policy scopes. Without an active manifest, a domain hub is an unregistered participant. Its widgets and events are accepted by the framework, but its type vocabulary is not namespaced, not validated, and not discoverable in Phase 10's marketplace. --- ## Schema ```sql CREATE TABLE hub_capability_manifests ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, hub_id UUID NOT NULL UNIQUE REFERENCES hubs(id), manifest_version TEXT NOT NULL DEFAULT '1.0', declared_widget_types JSONB NOT NULL DEFAULT '[]', declared_event_types JSONB NOT NULL DEFAULT '[]', declared_annotation_categories JSONB NOT NULL DEFAULT '[]', declared_policy_scopes JSONB NOT NULL DEFAULT '[]', capability_description TEXT, contact TEXT, status TEXT NOT NULL DEFAULT 'draft', activated_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL ); ``` --- ## Registration Workflow ``` 1. Create manifest in draft ↓ 2. Declare types (edit declared_* arrays) ↓ 3. Activate (ActivateManifestAction) ↓ ← auto-registers declared types into their registries 4. Active manifest governs the hub's vocabulary ``` ### Step 1 — Create in draft ``` POST /HubCapabilityManifests/new?hubId= ``` One manifest per hub (UNIQUE constraint on `hub_id`). Creating a manifest for a hub that already has one (in any status) requires retiring the existing active manifest first. ### Step 2 — Declare types While in `draft` status, edit the `declared_*` JSONB arrays. Each array contains the string names of types the hub will own: ```json { "declared_widget_types": ["dev-pipeline-run", "dev-build-status"], "declared_event_types": ["dev-pipeline-started", "dev-pipeline-failed"], "declared_annotation_categories": ["dev-blocker", "dev-flaky-test"], "declared_policy_scopes": ["dev-internal"] } ``` **Naming convention**: domain-owned types should be prefixed with the domain shortcode to prevent collisions (e.g. `dev-`, `fin-`, `sec-`, `ops-`). Framework-level types (no prefix) are owned by inter-hub and shared by all hubs. ### Step 3 — Activate ``` POST /HubCapabilityManifests/ActivateManifest?hubCapabilityManifestId= ``` On activation: - For each name in `declared_widget_types`: insert into `widget_type_registry` with `owner_hub_id = hub.id` if not already present. - Same for `declared_event_types`, `declared_annotation_categories`, `declared_policy_scopes`. - If any declared name already exists in a registry with a **different** `owner_hub_id`, activation is rejected with a conflict error. - If any declared name exists with `owner_hub_id = NULL` (framework-level), activation is rejected: framework types cannot be claimed by a domain hub. - `status` is set to `active`, `activated_at` is set to `now()`. --- ## Invariants 1. **Type names are permanent.** Once a type name is registered (either via seed or manifest activation), it cannot be deleted from the registry — only deprecated. Other hubs may already depend on it. 2. **Activated manifests are read-only on declared arrays.** To add new types, retire the manifest and create a new draft, or use `DraftAmendmentAction` which creates an amended draft pending re-activation. 3. **One active manifest per hub.** The UNIQUE constraint on `hub_id` plus the activation workflow enforce this. 4. **Framework types cannot be claimed.** Type names with `owner_hub_id = NULL` are owned by the framework. A domain hub manifest that attempts to declare an existing framework type name is rejected. --- ## Status Lifecycle ``` draft → active → retired ↓ (superseded by a new draft → active cycle) ``` A retired manifest's types remain in the registry. If the hub is decommissioned, the types should be deprecated (not deleted) in the registry. --- ## Failure Modes | Scenario | Behaviour | |---|---| | Duplicate type name (same hub) | Idempotent — skipped, not an error | | Duplicate type name (different hub) | Activation rejected with conflict error | | Framework type name claimed | Activation rejected | | Edit of active manifest declared arrays | Rejected — manifest is read-only | | Hub with no manifest creates hub-owned type | Warning in fitness function; types accepted but unmanifested | --- ## Implementation Reference - Schema: `Application/Schema.sql` (added in IHUB-WP-0009-T05) - Controller: `Web/Controller/HubCapabilityManifests.hs` - Guide: `docs/domain-hub-extension-guide.md` - Phase 10 dependency: Hub Registry = active manifests + health snapshots