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

200 lines
8.3 KiB
Markdown

# 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
```sql
-- 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:
```json
// 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 `draft``active`.
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.