diff --git a/workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md b/workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md new file mode 100644 index 0000000..e64c279 --- /dev/null +++ b/workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md @@ -0,0 +1,370 @@ +--- +id: IHUB-WP-0006 +type: workplan +title: "IHF Phase 6 — Cross-Framework UI Adaptation Layer" +domain: inter_hub +repo: inter-hub +status: todo +owner: custodian +topic_slug: inter_hub +created: "2026-03-29" +updated: "2026-03-29" +state_hub_workstream_id: "" +--- + +# IHF Phase 6 — Cross-Framework UI Adaptation Layer + +## Goal + +Ensure semantic continuity while the UI stack diversifies. Phase 5 established +AI-assisted distillation within the IHP server-rendered surface. 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. + +All Phase 6 artifacts are formal contracts rather than free-form conventions. +A widget that participates via an adapter must honour the same identity, +traceability, and event-capture obligations as a native IHP widget. + +## Background + +Phases 1–5 are complete. The IHF core (widget registry, interaction events, +annotations, requirements, decisions, outcomes, agent assistance) is stable. + +The spec (§Phase 6) calls for: +- widget protocol adapters +- metadata emission standards +- client-side SDKs or thin adapters +- cross-framework annotation launcher +- standardized interaction reporting interface + +Artifacts introduced: `WidgetAdapterSpec`, `InteractionReportingContract`, +`EnvelopeEmissionContract`. + +Reference: `specs/InteractionHubFrameworkSpecification_v0.1.md` §Phase 6, +`docs/ihp-overview.md`, `docs/ihp-controllers-views-forms.md`. + +## Phase 6 Exit Criteria (from IHF spec §Phase 6) + +- New UI technologies can participate without bypassing the IHF core +- Widget identity remains stable across frontend evolution +- Annotations and interaction events remain compatible + +## Data Artifacts Introduced (Phase 6) + +`WidgetAdapterSpec`, `InteractionReportingContract`, `EnvelopeEmissionContract` + +--- + +## Tasks + +### T01 — Schema: WidgetAdapterSpec, InteractionReportingContract, EnvelopeEmissionContract + +```task +id: IHUB-WP-0006-T01 +status: todo +priority: high +state_hub_task_id: "" +``` + +Add Phase 6 tables to `Application/Schema.sql` and write migration: + +```sql +-- Describes how a specific UI technology (React, Vue, etc.) maps to IHF widget +-- protocol obligations — identity, envelope emission, event reporting. +CREATE TABLE widget_adapter_specs ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + name TEXT NOT NULL UNIQUE, -- e.g. "react-18", "vue-3", "web-component" + framework TEXT NOT NULL, -- e.g. "react", "vue", "vanilla" + version TEXT NOT NULL, -- adapter spec version, e.g. "1.0" + envelope_contract_id UUID REFERENCES envelope_emission_contracts(id), + reporting_contract_id UUID REFERENCES interaction_reporting_contracts(id), + status TEXT NOT NULL DEFAULT 'draft', + -- status values: draft | active | deprecated + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL +); + +CREATE INDEX widget_adapter_specs_framework_idx ON widget_adapter_specs (framework); +CREATE INDEX widget_adapter_specs_status_idx ON widget_adapter_specs (status); + +-- Formalises the rules for how a widget envelope must be emitted: +-- which attributes are required, their format, and version. +CREATE TABLE envelope_emission_contracts ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + contract_version TEXT NOT NULL UNIQUE, -- e.g. "1.0", "1.1" + required_attributes JSONB NOT NULL, + -- e.g. ["data-widget-id", "data-view-context", "data-hub-id"] + optional_attributes JSONB NOT NULL DEFAULT '[]', + validation_rules JSONB NOT NULL DEFAULT '{}', + -- machine-readable rules: format checks, presence guards + description TEXT, + status TEXT NOT NULL DEFAULT 'active', + -- status values: draft | active | superseded + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL +); + +-- Standardised REST interface contract for external event and annotation +-- submission — used by non-IHP adapters. +CREATE TABLE interaction_reporting_contracts ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + contract_version TEXT NOT NULL UNIQUE, -- e.g. "1.0" + endpoint_path TEXT NOT NULL, -- e.g. "/api/v1/interaction-events" + accepted_event_types JSONB NOT NULL, -- e.g. ["clicked","viewed","submitted"] + required_fields JSONB NOT NULL, + -- minimum payload: widget_id, hub_id, event_type, occurred_at + auth_scheme TEXT NOT NULL DEFAULT 'bearer', + description TEXT, + status TEXT NOT NULL DEFAULT 'active', + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL +); + +-- Link widgets to their adapter spec (null = native IHP widget). +ALTER TABLE widgets + ADD COLUMN adapter_spec_id UUID REFERENCES widget_adapter_specs(id); + +CREATE INDEX widgets_adapter_spec_id_idx ON widgets (adapter_spec_id); +``` + +**Exit criteria:** `migrate` runs cleanly; all Phase 6 types available in GHCi. + +--- + +### T02 — EnvelopeEmissionContract: formalise widgetEnvelope as a versioned contract + +```task +id: IHUB-WP-0006-T02 +status: todo +priority: high +state_hub_task_id: "" +``` + +1. Seed the canonical v1.0 `EnvelopeEmissionContract` record in a migration: + - `required_attributes: ["data-widget-id", "data-view-context", "data-hub-id"]` + - `optional_attributes: ["data-policy-scope", "data-widget-version"]` + - `validation_rules: {data-widget-id: "uuid", data-hub-id: "uuid"}` +2. Update the `widgetEnvelope` helper (`Web/View/Helpers.hs` or equivalent) to + read the active contract version from DB (or config) and assert required + attributes at render time — log a warning (not crash) if any are missing. +3. Add `EnvelopeEmissionContractsController`: + - `index`: table of contract versions with status badges + - `show`: full required/optional attributes and validation rules as formatted + JSON panels + - Read-only (contracts are immutable once active; a new version supersedes) +4. Link from global nav under "Contracts" + +**Exit criteria:** Active contract record exists in DB; widgetEnvelope validates +against it; contract index/show pages render correctly. + +--- + +### T03 — InteractionReportingContract: REST endpoint for external event submission + +```task +id: IHUB-WP-0006-T03 +status: todo +priority: high +state_hub_task_id: "" +``` + +1. Seed the canonical v1.0 `InteractionReportingContract`: + - `endpoint_path: "/api/v1/interaction-events"` + - `accepted_event_types: ["clicked","viewed","submitted","dismissed","errored"]` + - `required_fields: ["widget_id","hub_id","event_type","occurred_at"]` +2. Add `Api.InteractionEventsController` (separate from the web controller): + - `POST /api/v1/interaction-events` — JSON body, Bearer token auth + - Validate payload against the active `InteractionReportingContract` + - Create `InteractionEvent` record + - Return `201 Created` with `{id, widget_id, event_type}` or `422` with + validation errors +3. Register the API route in `FrontController.hs` +4. Add `InteractionReportingContractsController` (read-only, same pattern as T02) + +**Exit criteria:** `POST /api/v1/interaction-events` with a valid payload creates +an `InteractionEvent`; invalid payloads return `422`; contract show page renders. + +--- + +### T04 — WidgetAdapterSpecsController and registry dashboard + +```task +id: IHUB-WP-0006-T04 +status: todo +priority: high +state_hub_task_id: "" +``` + +1. Scaffold `WidgetAdapterSpecsController`: + - `index`: table of adapters — framework badge, version, status, envelope + contract version, reporting contract version + - `new` / `create`: register a new adapter spec + - `show`: full detail — framework, version, linked contracts, notes, status + - `edit` / `update`: update notes and status only (contracts are immutable + once linked) + - No delete — adapter specs are audit artifacts +2. Validation: + - `name`, `framework`, `version` required + - `status` must be `draft | active | deprecated` +3. On widget `new`/`edit` forms: optional `adapter_spec_id` select (null = native) +4. On widget show page: if `adapter_spec_id` present, show adapter badge with + link to the spec + +**Exit criteria:** Adapter specs can be registered, listed, and viewed; widget +form allows adapter assignment; widget show page renders adapter badge. + +--- + +### T05 — Cross-framework annotation launcher (lightweight JS widget) + +```task +id: IHUB-WP-0006-T05 +status: todo +priority: medium +state_hub_task_id: "" +``` + +1. Create `static/js/ihf-annotation-launcher.js` — a self-contained vanilla JS + module (no framework dependency): + - On `DOMContentLoaded`, scan for elements with `data-widget-id` attribute + - Inject a small "annotate" trigger (button or icon) adjacent to each + enrolled element + - On trigger click: open a lightweight inline form (textarea + category + select) and POST to `/annotations` (existing IHP endpoint) via `fetch` + - On success: show a brief confirmation; on error: show inline error message + - Reads `data-hub-id` from the element (or nearest ancestor) for the hub + context +2. The launcher must work in React-rendered pages where IHP does not own the + DOM — it relies solely on `data-widget-id` presence. +3. Include as an optional script tag in the IHP layout (`Web/View/Layout.hs`) + with a feature flag (`IHP_ANNOTATION_LAUNCHER=true`) +4. Document usage in `docs/annotation-launcher.md` + +**Exit criteria:** Launcher script injects annotation triggers on a page with +`data-widget-id` elements; annotation POST succeeds; works from a static HTML +test page (not IHP-rendered). + +--- + +### T06 — React adapter specification and reference example + +```task +id: IHUB-WP-0006-T06 +status: todo +priority: medium +state_hub_task_id: "" +``` + +1. Register a `react-18` `WidgetAdapterSpec` record (via migration seed or + admin UI): + - links to envelope v1.0 contract and reporting v1.0 contract + - `status = active` +2. Create `static/js/ihf-react-adapter.js` — a thin React hook + HOC: + - `useWidgetEnvelope(widgetId, hubId, viewContext)` — returns a `ref` and + `data-*` props object conforming to the envelope contract + - `withWidgetEnvelope(WrappedComponent, widgetId, hubId, viewContext)` — HOC + that applies the envelope to the root DOM element + - `useInteractionReporter(widgetId, hubId)` — returns a `reportEvent(type)` + function that POSTs to `/api/v1/interaction-events` +3. Create `docs/react-adapter.md` with usage examples for all three exports +4. Add a test fixture page in `static/` demonstrating a React widget using the + adapter alongside an IHP-rendered widget on the same page + +**Exit criteria:** `useWidgetEnvelope` emits correct `data-*` attributes; +`reportEvent` reaches `/api/v1/interaction-events`; annotation launcher script +picks up the React widget's `data-widget-id`; docs written. + +--- + +### T07 — Adapter compatibility validation dashboard + +```task +id: IHUB-WP-0006-T07 +status: todo +priority: medium +state_hub_task_id: "" +``` + +1. Add `AdapterCompatibilityDashboardAction { hubId }` to `HubsController` + (AutoRefresh): + - **Adapter summary**: count of registered adapters by status + (draft / active / deprecated) + - **Widget coverage**: total widgets / native IHP / adapter-backed (per + adapter spec), with percentage bars + - **Contract versions in use**: which envelope and reporting contract + versions are active + - **Unassigned widgets**: widgets with no `adapter_spec_id` that have + received events from external origins (heuristic: `user_agent` not + matching IHP server) + - **Stale adapters**: adapter specs with `status=active` but no widgets + assigned in the last 30 days +2. Link from hub Show page alongside Triage / Governance / Antifragility / + Agent dashboards +3. Add "Adapters" link to global nav + +**Exit criteria:** Dashboard renders all five panels; live-updates on widget or +adapter changes; stale adapter detection works. + +--- + +### T08 — Phase 6 gate: tests, consistency, docs + +```task +id: IHUB-WP-0006-T08 +status: todo +priority: high +state_hub_task_id: "" +``` + +1. **Integration tests** (`Test/`): + - EnvelopeEmissionContract create + fetch (required_attributes, validation_rules) + - InteractionReportingContract create + fetch + - `POST /api/v1/interaction-events` — valid payload creates InteractionEvent + - `POST /api/v1/interaction-events` — missing required field returns 422 + - WidgetAdapterSpec create + status transition (draft → active → deprecated) + - Widget with adapter_spec_id: fetch + show renders adapter badge + - Adapter compatibility dashboard: compiles and returns correct widget counts +2. **Consistency sync** via State Hub MCP: + `check_repo_consistency(repo_slug="inter-hub", fix=True)` +3. **Documentation updates:** + - Update `SCOPE.md` current state section: Phase 6 complete + - Write `docs/phase6-summary.md`: what was built, contract model, adapter + pattern, known limitations, Phase 7 readiness +4. **Smoke test checklist:** + - Register a `react-18` adapter spec via UI + - Assign a widget to the adapter + - POST a test interaction event via `curl` to `/api/v1/interaction-events` + - Verify event appears in widget show page + - Open annotation launcher on a page with a React-backed widget + - Confirm adapter compatibility dashboard shows correct coverage + +**Exit criteria:** All tests pass; consistency sync reports no errors; smoke +test completed; SCOPE.md updated. + +--- + +## Phase 6 Dependencies + +- Phases 1–5 schema stable (widget registry, interaction events, and annotation + model required for adapter integration) +- `envelope_emission_contracts` and `interaction_reporting_contracts` must exist + before `widget_adapter_specs` (foreign key; T01 handles both in one migration) +- Contracts (T01–T03) before adapter spec controller (T04) +- Adapter spec controller (T04) before annotation launcher (T05) and React + adapter (T06) — widget assignment UI depends on T04 +- All feature tasks (T01–T07) before gate (T08) + +## Notes + +- **Contracts are immutable once active.** A new version supersedes the old; + old versions remain readable for audit. No in-place edits after status=active. +- **Native IHP widgets are unaffected.** `adapter_spec_id` is nullable. Existing + widgets continue to function exactly as before. +- **The JS adapter is a thin client.** It does not embed a framework build + pipeline. `ihf-react-adapter.js` is a plain ESM module; consumers bundle it + themselves. +- **Auth for the reporting API.** Bearer token scheme. In Phase 6 the token + is a per-hub API key stored in `hubs.api_key` (add column in T01 migration). + Phase 8 (federated) can layer on OAuth. +- **No local JS build toolchain added.** Static JS files are served as-is. + Phase 6 does not introduce npm, webpack, or esbuild into the IHP project.