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

98 lines
5.8 KiB
Markdown

# 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`