# Phase 8 Summary — Federated Hub Maturity Phase 8 completes the IHF v0.1 specification. It introduces the governance structures needed when multiple teams, hubs, and policies must coexist at organisational scale. ## What Was Built ### Delegated Ownership (`WidgetOwnership`) Every widget can now carry an explicit ownership record: `local` (owned by its hub), `delegated` (steward hub differs from owner hub), or `global` (org-wide). Ownership records are append-only audit artefacts — `effective_until` signals expiry, but records are never deleted. The ownership badge appears on the widget show page (colour-coded: local=gray, delegated=blue, global=purple). ### Inter-Hub Requirement Routing (`HubRoutingRule`) A priority-ordered rule engine routes `RequirementCandidate` records across hub boundaries. When a candidate is created, the engine finds the highest-priority active rule whose `match_category` and `match_widget_type` match (null = any) and sets `routed_to_hub_id` on the candidate. `RouteNowAction` allows manual re-evaluation. `RoutedCandidatesAction { hubId }` shows all candidates forwarded to a given hub from any source hub. ### Federated Policy Overlays (`FederatedPolicyOverlay`) Org-wide governance policies applied across selected hubs (or all hubs via `applies_to_hubs = []`). Overlays follow a `draft → active → retired` lifecycle. **Immutability pattern:** once activated, an overlay cannot be edited. A new overlay must be created to supersede the old one. The old overlay remains readable for audit. This mirrors the Phase 6 `EnvelopeEmissionContract` immutability pattern. The Policy Compliance Dashboard shows coverage metrics: decisions referencing at least one `PolicyReference` as a percentage of total in-scope decisions. ### Stewardship Roles (`StewardshipRole`) Named governance roles assigned to hubs (e.g. "Hub Lead", "Policy Steward", "Triage Owner"). Roles have `granted_at` and `revoked_at` timestamps. Contextual steward queries use the point-in-time pattern: `granted_at ≤ T AND (revoked_at IS NULL OR revoked_at > T)`. No edits — create a new record to replace a role. ### Archival and Lineage Inspection (`ArchiveRecord`, `is_archived`) **Soft-delete pattern:** `is_archived BOOLEAN NOT NULL DEFAULT FALSE` on `widgets`. Active queries filter with `filterWhere (#isArchived, False)`. The widget row and all related records are preserved. `ArchiveWidgetAction` sets the flag and creates an `ArchiveRecord` (subject_type, subject_id, reason, archived_by, lineage_ref). `LineageInspectorAction { widgetId }` renders the full IHF traceability chain in a single read-only timeline: `Widget → InteractionEvents → Annotations → RequirementCandidates → Requirements → DecisionRecords → DeploymentRecords → OutcomeSignals` plus any `ArchiveRecord` for the widget. ### Federated Governance Dashboard `FederatedGovernanceDashboardAction` (autoRefresh, five panels): | Panel | Metric | |-------|--------| | 1 — Ownership | % of widgets with ownership records; breakdown by type | | 2 — Routing | Active rule count; candidates routed cross-hub in 30 days | | 3 — Policy compliance | Active overlays; % decisions with policy reference | | 4 — Stewardship | Hubs with ≥1 active steward; hubs with no stewards | | 5 — Archive activity | Artifact counts archived in last 90 days by subject type | ## Schema Changes ```sql widget_ownerships -- delegated ownership records hub_routing_rules -- inter-hub routing logic requirement_candidates.routed_to_hub_id -- routing destination (nullable) federated_policy_overlays -- immutable org-wide policies stewardship_roles -- point-in-time governance roles archive_records -- soft-delete audit trail widgets.is_archived -- soft-delete flag ``` Migration: `Application/Migration/1743638400-ihf-phase8-federated-hub-maturity.sql` ## Routing Engine `Application/Helper/RoutingEngine.hs` — `applyRoutingRules`: ```haskell ruleMatches category mWidgetType rule = categoryMatch && widgetTypeMatch where categoryMatch = isNothing rule.matchCategory || rule.matchCategory == Just category widgetTypeMatch = isNothing rule.matchWidgetType || (isJust mWidgetType && rule.matchWidgetType == mWidgetType) ``` Null-inclusive matching: a rule with no `match_category` fires on any category. Only the highest-priority active matching rule fires per candidate. ## Known Limitations - `applies_to_hubs` on `FederatedPolicyOverlay` is stored as JSONB; Phase 8 does not enforce referential integrity between hub IDs in this column and the `hubs` table. A future phase could validate on activation. - `LineageInspectorAction` is widget-scoped. A fully generic artefact-scoped lineage inspector (for decisions, deployments, etc.) is a Phase 9+ feature. - Routing is evaluated on candidate creation and on manual `RouteNowAction`. There is no background re-evaluation if rules change after candidates exist. - Ownership records have no uniqueness constraint — multiple active ownerships per widget are possible. The latest `effective_from` record is authoritative by convention. ## IHF v0.1 Status All eight phases of `specs/InteractionHubFrameworkSpecification_v0.1.md` are now implemented in the reference IHP application. See `specs/InteractionHubFrameworkSpecification_v0.2.md` for the planned Phases 9–12 roadmap (External API, Marketplace, AI Federation, Platform Memory).