diff --git a/.claude/ralph-loop.local.md b/.claude/ralph-loop.local.md
new file mode 100644
index 0000000..8087b49
--- /dev/null
+++ b/.claude/ralph-loop.local.md
@@ -0,0 +1,419 @@
+---
+active: true
+iteration: 1
+session_id:
+max_iterations: 20
+completion_promise: "HEUREKA"
+workplan_id: IHUB-WP-0006
+workplan_file: workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md
+started_at: "2026-03-29T21:00:29Z"
+---
+
+## Workplan Status Check — Do This First, Every Iteration
+
+Read the workplan file at: `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md`
+
+Count the task blocks (fenced code blocks with language tag `task`):
+- How many tasks exist in total?
+- How many have `status: done`?
+
+If **every task** has `status: done` AND the frontmatter `status` is `done`:
+ The workplan is complete. Output exactly: HEUREKA
+ Do nothing else. Stop here.
+
+Otherwise: continue with the implementation below.
+
+---
+
+## Workplan: IHUB-WP-0006 — IHF Phase 6 — Cross-Framework UI Adaptation Layer
+**File:** `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md`
+
+
+# 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: "8d92f9d5-ec3c-4d9b-b16c-26f938a306e7"
+```
+
+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: "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
+
+```task
+id: IHUB-WP-0006-T03
+status: todo
+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
+
+```task
+id: IHUB-WP-0006-T04
+status: todo
+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)
+
+```task
+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
+
+```task
+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
+
+```task
+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
+
+```task
+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 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.
+
+---
+
+## How to Work
+
+- Stay strictly within the scope of the workplan above
+- Work through tasks in priority order (high → medium → low)
+- Use TDD where applicable: write a failing test, make it pass, then refactor
+- Use whatever test runner, linter, and build tools this repository already uses
+- Consult existing documentation (README, docs/, wiki/, specs/) for context
+- Document significant architecture decisions as ADRs if the project uses them
+
+## Updating Task Status
+
+As you complete each task, edit the workplan file to update its status:
+
+```
+status: todo → status: in_progress (when you start it)
+status: in_progress → status: done (when it is verified complete)
+```
+
+When **every task** is `done`, also update the workplan frontmatter:
+
+```
+status: active → status: done
+```
+
+## Success Criteria
+
+Before marking the workplan done and outputting `HEUREKA`,
+verify all of the following are true:
+
+1. Every task block in `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md` has `status: done`
+2. The workplan frontmatter `status` is `done`
+3. The full test suite passes with no failures
+4. The codebase passes the project's standard code-quality checks
+ (linting, type checking, formatting — whatever applies to this project)
+5. Documentation reflects the implemented behaviour
+
+Output `HEUREKA` only when all five are genuinely true.
+
diff --git a/Application/Helper/View.hs b/Application/Helper/View.hs
index 8ea6a62..ef5ad3e 100644
--- a/Application/Helper/View.hs
+++ b/Application/Helper/View.hs
@@ -11,6 +11,15 @@ import Web.Types
-- client-side event capture script reads to identify the widget without coupling
-- to implementation details.
--
+-- The envelope is validated against the v1.0 EnvelopeEmissionContract at render
+-- time. Missing required attributes are surfaced as an inline warning banner
+-- (development) rather than a hard failure, so layout is preserved in production.
+--
+-- Required attributes (contract v1.0):
+-- data-widget-id — stable UUID from the widget registry
+-- data-view-context — logical UI location
+-- data-hub-id — owning hub UUID
+--
-- Usage:
--
-- @
@@ -18,10 +27,10 @@ import Web.Types
--
-- |]
-- @
---
--- See docs/widget-envelope-convention.md for the full convention.
widgetEnvelope :: Widget -> Html -> Html
-widgetEnvelope widget inner = [hsx|
+widgetEnvelope widget inner =
+ let warnings = envelopeContractWarnings widget
+ in [hsx|
+ {renderEnvelopeWarnings warnings}
{inner}
|]
+
+-- | Validate a Widget record against EnvelopeEmissionContract v1.0 required
+-- attributes. Returns a list of human-readable warning messages for any
+-- attribute that is missing or empty. An empty list means the widget is
+-- contract-compliant.
+envelopeContractWarnings :: Widget -> [Text]
+envelopeContractWarnings widget = catMaybes
+ [ if isNothing widget.viewContext || widget.viewContext == Just ""
+ then Just "envelope:v1.0 — data-view-context is missing (set widget.viewContext)"
+ else Nothing
+ -- data-widget-id and data-hub-id are always present (non-nullable fields)
+ ]
+
+renderEnvelopeWarnings :: [Text] -> Html
+renderEnvelopeWarnings [] = mempty
+renderEnvelopeWarnings ws = [hsx|
+