Files
inter-hub/workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md
Bernd Worsch 04eb4643b0 feat(P6/T05): cross-framework annotation launcher JS widget
ihf-annotation-launcher.js: vanilla JS, no framework dependency. Scans DOM for
data-widget-id elements, injects Annotate trigger, opens inline form, POSTs to
/widgets/:widgetId/annotations. Works in React/Vue-rendered pages via
MutationObserver. Feature-gated by IHP_ANNOTATION_LAUNCHER=true env var
(Config.hs AnnotationLauncherEnabled, FrontController layout conditional).
Docs: docs/annotation-launcher.md.

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

15 KiB
Raw Blame History

id, type, title, domain, repo, status, owner, topic_slug, created, updated, state_hub_workstream_id
id type title domain repo status owner topic_slug created updated state_hub_workstream_id
IHUB-WP-0006 workplan IHF Phase 6 — Cross-Framework UI Adaptation Layer inter_hub inter-hub todo custodian inter_hub 2026-03-29 2026-03-29 7372340a-0fcb-4911-8831-2a55ed9069a4

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 15 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

id: IHUB-WP-0006-T01
status: done
priority: high
state_hub_task_id: "8d92f9d5-ec3c-4d9b-b16c-26f938a306e7"

Add Phase 6 tables to Application/Schema.sql and write migration:

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

id: IHUB-WP-0006-T02
status: done
priority: high
state_hub_task_id: "298af675-550b-480b-bed6-05efc79cd0c9"
  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

id: IHUB-WP-0006-T03
status: done
priority: high
state_hub_task_id: "f2767465-ff00-48be-b2dc-5bf3b179cca9"
  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

id: IHUB-WP-0006-T04
status: done
priority: high
state_hub_task_id: "e84016d0-60c0-48cb-ad70-2c054d2530db"
  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)

id: IHUB-WP-0006-T05
status: todo
priority: medium
state_hub_task_id: "fea86955-d5e6-4623-b5cc-f422c266c9cf"
  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

id: IHUB-WP-0006-T06
status: todo
priority: medium
state_hub_task_id: "023269d8-9835-40b4-a394-478a0f36eee0"
  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

id: IHUB-WP-0006-T07
status: todo
priority: medium
state_hub_task_id: "dc8fa48a-7195-4410-a77e-717b53127c2e"
  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

id: IHUB-WP-0006-T08
status: todo
priority: high
state_hub_task_id: "90ea4814-7603-4016-be34-d41ae091f7e1"
  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 15 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 (T01T03) 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 (T01T07) 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.