Files
inter-hub/Web/FrontController.hs
Bernd Worsch 9265ca2d9c feat(P8): IHF Phase 8 complete — Federated Hub Maturity
Implements the final phase of the IHF v0.1 specification:

- WidgetOwnership: delegated ownership registry (local/delegated/global),
  append-only audit artefacts, ownership badge on widget show page
- HubRoutingRule + RoutingEngine: priority-ordered inter-hub routing engine;
  null-inclusive category/widget-type matching; RouteNowAction for manual
  re-evaluation; RoutedCandidates view per hub
- FederatedPolicyOverlay: draft → active → retired lifecycle; activated
  overlays are immutable (same pattern as Phase 6 contracts); policy
  compliance dashboard with decision coverage metrics
- StewardshipRole: named governance roles per hub; point-in-time revocation
  pattern; hub and ops-board integration
- ArchiveRecord + is_archived: soft-delete on widgets; lineage inspector
  traces full traceability chain (Widget → Events → Annotations → Candidates
  → Requirements → Decisions → Deployments → Signals + ArchiveRecord)
- FederatedGovernanceDashboard: 5-panel autoRefresh org-wide governance view
  (ownership coverage, routing activity, policy compliance, stewardship
  coverage, archive activity)

Schema: widget_ownerships, hub_routing_rules, federated_policy_overlays,
stewardship_roles, archive_records; ALTER widgets ADD is_archived;
ALTER requirement_candidates ADD routed_to_hub_id

Migration: 1743638400-ihf-phase8-federated-hub-maturity.sql
Tests: Phase 8 integration tests appended to Test/Integration.hs
Docs: docs/phase8-summary.md; SCOPE.md updated to Phase 8 complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 22:53:01 +00:00

113 lines
5.2 KiB
Haskell

module Web.FrontController where
import IHP.RouterPrelude
import IHP.LoginSupport.Middleware
import IHP.ControllerPrelude (getAppConfig)
import Generated.Types
import Web.Types
import Web.Routes ()
import Config (AnnotationLauncherEnabled (..))
-- Controllers
import Web.Controller.Hubs ()
import Web.Controller.Widgets ()
import Web.Controller.InteractionEvents ()
import Web.Controller.Annotations ()
import Web.Controller.AnnotationThreads ()
import Web.Controller.RequirementCandidates ()
import Web.Controller.Requirements ()
import Web.Controller.DecisionRecords ()
import Web.Controller.DeploymentRecords ()
import Web.Controller.AgentProposals ()
import Web.Controller.ApiInteractionEvents ()
import Web.Controller.EnvelopeEmissionContracts ()
import Web.Controller.InteractionReportingContracts ()
import Web.Controller.WidgetAdapterSpecs ()
import Web.Controller.CrossHubPropagations ()
import Web.Controller.WidgetOwnerships ()
import Web.Controller.HubRoutingRules ()
import Web.Controller.FederatedPolicyOverlays ()
import Web.Controller.StewardshipRoles ()
import Web.Controller.ArchiveRecords ()
import Web.Controller.FederatedGovernance ()
import Web.Controller.Sessions ()
instance FrontController WebApplication where
controllers =
[ parseRoute @SessionsController
, parseRoute @HubsController
, parseRoute @WidgetsController
, parseRoute @InteractionEventsController
, parseRoute @AnnotationsController
, parseRoute @AnnotationThreadsController
, parseRoute @RequirementCandidatesController
, parseRoute @RequirementsController
, parseRoute @DecisionRecordsController
, parseRoute @DeploymentRecordsController
, parseRoute @AgentProposalsController
, parseRoute @ApiInteractionEventsController
, parseRoute @EnvelopeEmissionContractsController
, parseRoute @InteractionReportingContractsController
, parseRoute @WidgetAdapterSpecsController
, parseRoute @CrossHubPropagationsController
, parseRoute @WidgetOwnershipsController
, parseRoute @HubRoutingRulesController
, parseRoute @FederatedPolicyOverlaysController
, parseRoute @StewardshipRolesController
, parseRoute @ArchiveRecordsController
, parseRoute @FederatedGovernanceController
]
instance InitControllerContext WebApplication where
initContext = do
setLayout defaultLayout
initAuthentication @User
annotationLauncherScript :: (?context :: ControllerContext) => Html
annotationLauncherScript =
let AnnotationLauncherEnabled enabled = getAppConfig @AnnotationLauncherEnabled
in if enabled
then [hsx|<script src="/js/ihf-annotation-launcher.js"></script>|]
else mempty
defaultLayout :: Layout
defaultLayout inner = [hsx|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>inter-hub</title>
{autoRefreshMeta}
<link rel="stylesheet" href="/app.css" />
<script src="/vendor/morphdom.js"></script>
<script src="/vendor/ihp-auto-refresh.js"></script>
{annotationLauncherScript}
</head>
<body class="bg-gray-50 text-gray-900">
<nav class="bg-white border-b border-gray-200 px-6 py-3 flex items-center gap-6">
<a href={HubsAction} class="font-semibold text-indigo-600">inter-hub</a>
<a href={HubsAction} class="text-sm text-gray-600 hover:text-gray-900">Hubs</a>
<a href={WidgetsAction} class="text-sm text-gray-600 hover:text-gray-900">Widgets</a>
<a href={RequirementCandidatesAction} class="text-sm text-gray-600 hover:text-gray-900">Candidates</a>
<a href={RequirementsAction} class="text-sm text-gray-600 hover:text-gray-900">Requirements</a>
<a href={DecisionRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Decisions</a>
<a href={DeploymentRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Deployments</a>
<a href={AgentProposalsAction} class="text-sm text-gray-600 hover:text-gray-900">Agent</a>
<a href={WidgetAdapterSpecsAction} class="text-sm text-gray-600 hover:text-gray-900">Adapters</a>
<a href={CrossHubPropagationsAction} class="text-sm text-gray-600 hover:text-gray-900">Propagations</a>
<a href={OperationalReviewBoardAction} class="text-sm text-gray-600 hover:text-gray-900">Ops Review</a>
<a href={FederatedGovernanceDashboardAction} class="text-sm text-gray-600 hover:text-gray-900">Federation</a>
<a href={FederatedPolicyOverlaysAction} class="text-sm text-gray-600 hover:text-gray-900">Policies</a>
<a href={ArchiveRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Archive</a>
<div class="ml-auto">
<a href={DeleteSessionAction} class="text-sm text-gray-500 hover:text-gray-700">Sign out</a>
</div>
</nav>
<main class="max-w-5xl mx-auto px-6 py-8">
{inner}
</main>
</body>
</html>
|]