chore: register Phase 6 workplan (IHUB-WP-0006)

Cross-Framework UI Adaptation Layer — 8 tasks (T01–T08).
State Hub workstream/task IDs pending registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 20:34:59 +00:00
parent a7ee8d414e
commit e3a9fb07e1

View File

@@ -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 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
```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 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.