Files
inter-hub/docs/domain-hub-extension-guide.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

8.3 KiB

Domain Hub Extension Guide

Framework: IHF v0.2 + GAAF-2026 | Applies to: Phase 9+ domain hub implementations

This guide is for developers building a new domain hub (dev-hub, ops-hub, fin-hub, sec-hub) on top of inter-hub. It covers what the framework provides out of the box, how to register your domain's vocabulary, and how to stay compatible across framework upgrades.


What inter-hub Provides

Every domain hub built on inter-hub inherits the following services without any setup:

Service What it gives you
Event capture POST /api/v1/interaction-events — record user interactions with a hub API key
Annotation Structured feedback on any widget (friction, defects, wishes, policy concerns)
Requirement candidates Automatic escalation from annotations; triage queue and lifecycle
Governance ledger Decision records, rationale, and approved/rejected outcomes
AI assistance Claude-powered summarization and requirement drafting via AgentProposal
Deployment + signals DeploymentRecord → OutcomeSignal; regression detection
Observability FrictionScore, BottleneckRecord, HubHealthSnapshot
Federation CrossHubPropagation, WidgetOwnership, FederatedPolicyOverlay
Cross-framework adapters EnvelopeEmissionContract, InteractionReportingContract
Archive + lineage Soft-delete with full lineage inspector

None of these require domain-specific configuration. They are activated as soon as you create a Hub row and register Widget records.


Extension Registration in Three Steps

Domain hubs introduce vocabulary that the framework does not know about at installation time: domain-specific widget types (e.g. dev-pipeline-run), event types (e.g. fin-budget-alert-dismissed), annotation categories, and policy scopes.

You must register this vocabulary before widgets or events using these names can be validated by the framework.

Step 1 — Create a Hub row

-- Via the IHP UI at /hubs/new, or via a migration:
INSERT INTO hubs (id, name, slug, domain, hub_kind)
VALUES (uuid_generate_v4(), 'Dev Hub', 'dev-hub', 'dev.example.com', 'domain');
  • hub_kind must be 'domain' for bounded domain hubs, or 'shared' for cross-domain service hubs. 'framework' is reserved for inter-hub itself.
  • There is exactly one framework hub (enforced by a unique partial index).

Step 2 — Create a HubCapabilityManifest in draft

Navigate to Extensions → New Manifest in the inter-hub UI, or via the API:

POST /HubCapabilityManifests (CreateHubCapabilityManifestAction)
hubId=<your hub id>
capabilityDescription=Developer toolchain interaction tracking
contact=platform-team@example.com

In the manifest Edit view, declare your type names as JSON arrays:

// Declared Widget Types
["dev-pipeline-run", "dev-pr-review", "dev-build-status"]

// Declared Event Types
["dev-pipeline-triggered", "dev-build-failed", "dev-pr-approved"]

// Declared Annotation Categories
["dev-flaky-test", "dev-merge-concern"]

// Declared Policy Scopes
["dev-ci-policy"]

Step 3 — Activate the manifest

Click Activate in the manifest UI, or:

GET /HubCapabilityManifests/{id}/activate (ActivateManifestAction)

On activation, the framework:

  1. Validates that each declared type name is either unregistered or already owned by your hub.
  2. If any name is owned by a different hub, activation is blocked with a conflict message.
  3. If all names are clear, each declared name is inserted into its registry table (widget_type_registry, event_type_registry, etc.) with owner_hub_id = your hub id.
  4. The manifest status transitions from draftactive.

After activation, your types are:

  • Validated by all IHF controllers (widgets, annotations, routing rules, API events)
  • Enumerable by the Phase 9 OpenAPI specification
  • Discoverable by other hubs via the Extensions page

Naming Your Types

Context Convention Example
Framework-level types No prefix, lowercase-hyphenated chart, form, clicked
Domain-owned types Prefixed with domain shortcode dev-pipeline-run, fin-budget-alert
Shared hub types Prefixed with service shortcode state-workstream, gov-decision

Rules:

  • Names are permanent — once registered, they cannot be deleted or renamed. Use status = 'deprecated' with a deprecated_in_favour_of pointer if you need to transition to a new name.
  • Names are globally unique per registry table. Two hubs cannot own the same name. Use domain prefixes to avoid collisions.
  • Names must be lowercase. Hyphens are preferred over underscores for widget/event types; underscores are accepted for annotation categories and policy scopes.

Using Framework-Level Types

Framework-level types (those with owner_hub_id IS NULL) are available to all hubs without any registration. You do not need to declare them in your manifest.

Examples of framework-level widget types: chart, form, table, action, panel, nav Examples of framework-level event types: clicked, viewed, submitted, dismissed, errored

Check the Type Registries UI (/TypeRegistries/WidgetTypes) to see all active framework types before creating domain-specific alternatives.


Cross-Hub Routing

If your domain hub should receive requirement candidates routed from other hubs, configure HubRoutingRule entries:

POST /HubRoutingRules (CreateHubRoutingRuleAction)
sourceHubId=<framework hub>
targetHubId=<your domain hub id>
matchCategory=dev-flaky-test
priority=10

The matchCategory and matchWidgetType fields are validated against their registries. You must activate your manifest before creating routing rules that reference your domain types.


Interpreting Maturity Labels

When depending on IHF modules in your domain hub, check docs/functional-modules.md for the current maturity of each module:

Maturity What it means for your hub
Stable Safe to depend on. Breaking changes require a major version bump with migration path.
Beta Core functionality is solid but edge-case fields or computation details may change with a minor-version notice. Suitable for production with awareness.
Experimental Internal prototyping only. May change without notice. Do not build production features on Experimental modules.
Deprecated Will be removed in the next major version. A replacement is documented in the relevant contract file.

The maturity badge is displayed on contract show pages in the inter-hub UI.


FAQ

Can two hubs declare the same type name? No. Type names are globally unique per registry. Activation order determines ownership. If you need a name that another hub owns, coordinate with that hub's contact to either share the type (they keep ownership, you use it) or use a domain-prefixed variant.

Can a type be renamed? No. Names are permanent. To transition: deprecate the old name with deprecated_in_favour_of pointing to the new name, then register the new name (via a new manifest entry or the Type Registry UI).

Can a hub retire its manifest? Yes. Retiring a manifest sets its status to retired; the hub's types remain in registries and continue to validate. Retiring is appropriate when a hub is decommissioned or merged. Orphaned types (owned by a retired hub's manifest) should be reassigned via the Type Registry UI or deprecated.

Do I need a manifest if I only use framework-level types? No. A manifest is only needed if your hub introduces new type names. If you use only existing framework types, you can create widgets and events immediately.

What if my manifest activation fails with a conflict? The activation endpoint returns the conflicting type names and which hub owns them. Either rename your types (before activation; names in a draft manifest can still be changed) or coordinate with the owning hub.

Can I add new types to an active manifest? Currently, activated manifests are read-only on the declared_* arrays. The amendment workflow is: retire the active manifest, create a new draft, declare all types (old + new), activate. A DraftAmendmentAction that merges types without retiring is planned for Phase 10.