Files
inter-hub/docs/phase6-summary.md
Bernd Worsch b1d1f5066b feat(P6): IHF Phase 6 complete — agent-assisted distillation
T08 gate: integration tests for all Phase 6 artifacts (EnvelopeEmissionContract,
InteractionReportingContract, WidgetAdapterSpec, adapter assignment, dashboard
coverage logic); SCOPE.md updated to Phase 6 complete; docs/phase6-summary.md
written (contract model, adapter pattern, known limitations, Phase 7 readiness);
workplan IHUB-WP-0006 marked done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:27:10 +00:00

5.8 KiB

IHF Phase 6 Summary — Cross-Framework UI Adaptation Layer

What Was Built

Phase 6 ensures that widget identity, interaction capture, and annotation capability are preserved when UI components are authored outside of IHP HSX — React, Vue, or any JS-based component — without bypassing the IHF core.

Contract Artifacts

EnvelopeEmissionContract (envelope_emission_contracts)

  • Formalises the widget envelope as a versioned, immutable contract
  • v1.0 seed: required ["data-widget-id", "data-view-context", "data-hub-id"], optional ["data-policy-scope", "data-widget-version"]
  • widgetEnvelope helper updated to validate required attributes at render time (non-crashing warning)
  • Read-only controller + index/show pages under "Contracts" nav

InteractionReportingContract (interaction_reporting_contracts)

  • Standardised REST interface contract for external event and annotation submission
  • v1.0 seed: endpoint /api/v1/interaction-events, accepted event types ["clicked","viewed","submitted","dismissed","errored"], required fields ["widget_id","hub_id","event_type","occurred_at"]
  • Read-only controller + index/show pages

WidgetAdapterSpec (widget_adapter_specs)

  • Registry of supported UI framework adapters; links to envelope and reporting contracts
  • Status lifecycle: draft → active → deprecated
  • Full CRUD controller (no delete — audit artifact)
  • Widgets gain an optional adapter_spec_id FK (null = native IHP widget)

REST API

POST /api/v1/interaction-events — JSON body, Bearer token auth (per-hub api_key):

  • Validates payload against active InteractionReportingContract
  • Creates InteractionEvent record
  • Returns 201 Created with {id, widget_id, event_type, occurred_at} or 422 with errors
  • Custom CanRoute/HasPath instances (not AutoRoute) for the /api/v1/ path prefix

JavaScript Client

static/js/ihf-annotation-launcher.js — vanilla JS IIFE (no framework dependency):

  • Scans DOM for [data-widget-id] on DOMContentLoaded
  • Injects inline "annotate" trigger adjacent to each enrolled element
  • On click: opens an inline form (textarea + category select) and POSTs to existing /widgets/:widgetId/annotations endpoint
  • Works in React-rendered pages where IHP does not own the DOM
  • Feature-flagged via IHP_ANNOTATION_LAUNCHER=true env var (AnnotationLauncherEnabled typed config)

static/js/ihf-react-adapter.js — ESM module for React 18+:

  • useWidgetEnvelope(widgetId, hubId, viewContext) — returns data-* props conforming to envelope v1.0
  • withWidgetEnvelope(WrappedComponent, widgetId, hubId, viewContext) — HOC wrapping root element
  • useInteractionReporter(widgetId, hubId) — returns reportEvent(type) POSTing to /api/v1/interaction-events

static/ihf-react-test.html — static test fixture with CDN React 18, a React-rendered widget, and an IHP-rendered widget on the same page, both enrolled with the annotation launcher.

Dashboards and Navigation

Adapter Compatibility Dashboard (AdapterCompatibilityDashboardAction { hubId }):

  • Panel 1: Adapter spec counts by status (active / draft / deprecated)
  • Panel 2: Widget coverage — native IHP vs adapter-backed with percentage bar and per-spec breakdown
  • Panel 3: Active contract versions in use (envelope + reporting)
  • Panel 4: Unassigned widgets (no adapter_spec_id)
  • Panel 5: Active adapter specs table with widget counts
  • autoRefresh — live-updates on any DB change
  • Linked from hub Show page and global nav ("Adapters")

Widget show page renders an adapter badge (purple, links to spec) when adapter_spec_id is set.

Contract Model

Contracts are immutable once active. A new version supersedes the old; old versions remain readable for audit. The status lifecycle is:

draft → active → superseded  (EnvelopeEmissionContract, InteractionReportingContract)
draft → active → deprecated  (WidgetAdapterSpec)

This prevents retroactive mutation of the rules that governed historical interaction events.

Adapter Pattern

Any UI technology participates by honouring three obligations:

  1. Envelope — root DOM element carries data-widget-id, data-view-context, data-hub-id
  2. Event reporting — interactions submitted to POST /api/v1/interaction-events with bearer auth
  3. Annotation — annotation launcher picks up data-widget-id automatically; no adapter-specific code required

Native IHP widgets are unaffected. adapter_spec_id is nullable. The widgetEnvelope helper continues to work unchanged.

Known Limitations

  • No local JS build toolchain. ihf-react-adapter.js is a plain ESM module; consumers must bundle it themselves (e.g. Vite, esbuild). Phase 6 does not add npm/webpack to the IHP project.
  • Bearer token scope. The per-hub api_key is a simple secret stored in hubs.api_key. Phase 8 (federated) should replace this with OAuth or a proper token management service.
  • Annotation launcher requires no CSP violations. The inline form approach works in permissive CSP environments; strict script-src policies may require nonce injection.
  • Cross-machine consistency checks. The State Hub consistency checker runs on the server and cannot reach the local repo filesystem; C-01/C-07 errors in the check output are path false-positives for this deployment topology.

Phase 7 Readiness

Phase 6 leaves the IHF core stable and extensible:

  • All Phase 6 contracts are versioned and immutable — Phase 7 can introduce v1.1 contracts without breaking existing adapters
  • Widget adapter_spec_id is nullable and indexed — adapter assignment is opt-in
  • The REST API surface is defined by the InteractionReportingContract record — contract evolution is data-driven
  • The JS adapter is a thin ESM module — framework-specific adapters for Vue, Web Components, etc. follow the same pattern as ihf-react-adapter.js