Files
inter-hub/contracts/extensions/hub-capability-manifest-v1.md
Bernd Worsch b5d73aa18b
Some checks failed
Test / test (push) Has been cancelled
feat(WP-0009): IHF GAAF Compliance Foundation — type registries, extension manifests, architectural contracts
Implements IHUB-WP-0009: closes four GAAF-2026 gaps before domain hub work begins.
- TypeRegistry helper + controllers/views (hub_kind, hub_capability_manifest)
- HubCapabilityManifest entity with validation and registry linkage
- ARCHITECTURE-LAYERS.md + CI-enforced boundary contracts
- Alembic migration 1743724800, fitness tests (Test/Architecture/)
- GAAF spec, Operational Architecture spec, domain hub extension guide
- Updates to CLAUDE.md, SCOPE.md, Schema.sql, Routes, FrontController, Types

state_hub_sync: pending (tunnel was STALE at completion time; run fix-consistency)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:17:39 +00:00

5.0 KiB

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

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=<hub-id>

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:

{
  "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=<id>

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