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>
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 intowidget_type_registrywithowner_hub_id = hub.idif 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. statusis set toactive,activated_atis set tonow().
Invariants
-
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.
-
Activated manifests are read-only on declared arrays. To add new types, retire the manifest and create a new draft, or use
DraftAmendmentActionwhich creates an amended draft pending re-activation. -
One active manifest per hub. The UNIQUE constraint on
hub_idplus the activation workflow enforce this. -
Framework types cannot be claimed. Type names with
owner_hub_id = NULLare 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