generated from coulomb/repo-seed
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>
98 lines
5.8 KiB
Markdown
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`
|