generated from coulomb/repo-seed
feat(P6/T01): Phase 6 schema — WidgetAdapterSpec, contracts, widgets.adapter_spec_id
Adds Phase 6 tables: envelope_emission_contracts, interaction_reporting_contracts, widget_adapter_specs. Adds adapter_spec_id FK to widgets and api_key to hubs. Seeds v1.0 contracts in migration. Registers Phase 6 controller types and routes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
-- IHF Phase 6 — Cross-Framework UI Adaptation Layer
|
||||||
|
-- Adds: envelope_emission_contracts, interaction_reporting_contracts,
|
||||||
|
-- widget_adapter_specs, widgets.adapter_spec_id, hubs.api_key
|
||||||
|
-- Seeds: v1.0 contracts
|
||||||
|
|
||||||
|
CREATE TABLE envelope_emission_contracts (
|
||||||
|
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||||
|
contract_version TEXT NOT NULL UNIQUE,
|
||||||
|
required_attributes JSONB NOT NULL,
|
||||||
|
optional_attributes JSONB NOT NULL DEFAULT '[]',
|
||||||
|
validation_rules JSONB NOT NULL DEFAULT '{}',
|
||||||
|
description TEXT,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX envelope_emission_contracts_status_idx ON envelope_emission_contracts (status);
|
||||||
|
|
||||||
|
CREATE TABLE interaction_reporting_contracts (
|
||||||
|
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||||
|
contract_version TEXT NOT NULL UNIQUE,
|
||||||
|
endpoint_path TEXT NOT NULL,
|
||||||
|
accepted_event_types JSONB NOT NULL,
|
||||||
|
required_fields JSONB NOT NULL,
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX interaction_reporting_contracts_status_idx ON interaction_reporting_contracts (status);
|
||||||
|
|
||||||
|
CREATE TABLE widget_adapter_specs (
|
||||||
|
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
framework TEXT NOT NULL,
|
||||||
|
version TEXT NOT NULL,
|
||||||
|
envelope_contract_id UUID REFERENCES envelope_emission_contracts(id),
|
||||||
|
reporting_contract_id UUID REFERENCES interaction_reporting_contracts(id),
|
||||||
|
status TEXT NOT NULL DEFAULT 'draft',
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
ALTER TABLE hubs
|
||||||
|
ADD COLUMN api_key TEXT;
|
||||||
|
|
||||||
|
-- Seed: v1.0 EnvelopeEmissionContract
|
||||||
|
INSERT INTO envelope_emission_contracts (
|
||||||
|
contract_version,
|
||||||
|
required_attributes,
|
||||||
|
optional_attributes,
|
||||||
|
validation_rules,
|
||||||
|
description,
|
||||||
|
status
|
||||||
|
) VALUES (
|
||||||
|
'1.0',
|
||||||
|
'["data-widget-id", "data-view-context", "data-hub-id"]',
|
||||||
|
'["data-policy-scope", "data-widget-version"]',
|
||||||
|
'{"data-widget-id": "uuid", "data-hub-id": "uuid"}',
|
||||||
|
'Canonical IHF widget envelope contract v1.0. Requires widget identity, view context, and hub attribution on every rendered widget element.',
|
||||||
|
'active'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Seed: v1.0 InteractionReportingContract
|
||||||
|
INSERT INTO interaction_reporting_contracts (
|
||||||
|
contract_version,
|
||||||
|
endpoint_path,
|
||||||
|
accepted_event_types,
|
||||||
|
required_fields,
|
||||||
|
auth_scheme,
|
||||||
|
description,
|
||||||
|
status
|
||||||
|
) VALUES (
|
||||||
|
'1.0',
|
||||||
|
'/api/v1/interaction-events',
|
||||||
|
'["clicked", "viewed", "submitted", "dismissed", "errored"]',
|
||||||
|
'["widget_id", "hub_id", "event_type", "occurred_at"]',
|
||||||
|
'bearer',
|
||||||
|
'Canonical IHF interaction reporting contract v1.0. External adapters POST events to this endpoint using a hub-scoped bearer token.',
|
||||||
|
'active'
|
||||||
|
);
|
||||||
@@ -312,3 +312,70 @@ CREATE TABLE confidence_annotations (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX confidence_annotations_proposal_id_idx ON confidence_annotations (proposal_id);
|
CREATE INDEX confidence_annotations_proposal_id_idx ON confidence_annotations (proposal_id);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- Phase 6 — Cross-Framework UI Adaptation Layer
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- Formalises the rules for widget envelope emission: which data-* attributes
|
||||||
|
-- are required, their format, and the contract 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
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX envelope_emission_contracts_status_idx ON envelope_emission_contracts (status);
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX interaction_reporting_contracts_status_idx ON interaction_reporting_contracts (status);
|
||||||
|
|
||||||
|
-- Describes how a specific UI technology maps to IHF widget protocol obligations.
|
||||||
|
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);
|
||||||
|
|
||||||
|
-- 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);
|
||||||
|
|
||||||
|
-- Per-hub API key for bearer-token auth on the interaction reporting endpoint.
|
||||||
|
ALTER TABLE hubs
|
||||||
|
ADD COLUMN api_key TEXT;
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import Web.Controller.Requirements ()
|
|||||||
import Web.Controller.DecisionRecords ()
|
import Web.Controller.DecisionRecords ()
|
||||||
import Web.Controller.DeploymentRecords ()
|
import Web.Controller.DeploymentRecords ()
|
||||||
import Web.Controller.AgentProposals ()
|
import Web.Controller.AgentProposals ()
|
||||||
|
import Web.Controller.EnvelopeEmissionContracts ()
|
||||||
|
import Web.Controller.InteractionReportingContracts ()
|
||||||
|
import Web.Controller.WidgetAdapterSpecs ()
|
||||||
import Web.Controller.Sessions ()
|
import Web.Controller.Sessions ()
|
||||||
|
|
||||||
instance FrontController WebApplication where
|
instance FrontController WebApplication where
|
||||||
@@ -32,6 +35,9 @@ instance FrontController WebApplication where
|
|||||||
, parseRoute @DecisionRecordsController
|
, parseRoute @DecisionRecordsController
|
||||||
, parseRoute @DeploymentRecordsController
|
, parseRoute @DeploymentRecordsController
|
||||||
, parseRoute @AgentProposalsController
|
, parseRoute @AgentProposalsController
|
||||||
|
, parseRoute @EnvelopeEmissionContractsController
|
||||||
|
, parseRoute @InteractionReportingContractsController
|
||||||
|
, parseRoute @WidgetAdapterSpecsController
|
||||||
]
|
]
|
||||||
|
|
||||||
instance InitControllerContext WebApplication where
|
instance InitControllerContext WebApplication where
|
||||||
@@ -62,6 +68,7 @@ defaultLayout inner = [hsx|
|
|||||||
<a href={DecisionRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Decisions</a>
|
<a href={DecisionRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Decisions</a>
|
||||||
<a href={DeploymentRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Deployments</a>
|
<a href={DeploymentRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Deployments</a>
|
||||||
<a href={AgentProposalsAction} class="text-sm text-gray-600 hover:text-gray-900">Agent</a>
|
<a href={AgentProposalsAction} class="text-sm text-gray-600 hover:text-gray-900">Agent</a>
|
||||||
|
<a href={WidgetAdapterSpecsAction} class="text-sm text-gray-600 hover:text-gray-900">Adapters</a>
|
||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
<a href={DeleteSessionAction} class="text-sm text-gray-500 hover:text-gray-700">Sign out</a>
|
<a href={DeleteSessionAction} class="text-sm text-gray-500 hover:text-gray-700">Sign out</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,5 +34,10 @@ instance AutoRoute DeploymentRecordsController
|
|||||||
-- Agent Proposals (Phase 5)
|
-- Agent Proposals (Phase 5)
|
||||||
instance AutoRoute AgentProposalsController
|
instance AutoRoute AgentProposalsController
|
||||||
|
|
||||||
|
-- Phase 6 — Cross-Framework UI Adaptation
|
||||||
|
instance AutoRoute EnvelopeEmissionContractsController
|
||||||
|
instance AutoRoute InteractionReportingContractsController
|
||||||
|
instance AutoRoute WidgetAdapterSpecsController
|
||||||
|
|
||||||
-- Sessions
|
-- Sessions
|
||||||
instance AutoRoute SessionsController
|
instance AutoRoute SessionsController
|
||||||
|
|||||||
22
Web/Types.hs
22
Web/Types.hs
@@ -26,7 +26,8 @@ data HubsController
|
|||||||
| TriageDashboardAction { hubId :: !(Id Hub) }
|
| TriageDashboardAction { hubId :: !(Id Hub) }
|
||||||
| GovernanceDashboardAction { hubId :: !(Id Hub) }
|
| GovernanceDashboardAction { hubId :: !(Id Hub) }
|
||||||
| AntifragilityDashboardAction { hubId :: !(Id Hub) }
|
| AntifragilityDashboardAction { hubId :: !(Id Hub) }
|
||||||
| AgentAuditDashboardAction { hubId :: !(Id Hub) }
|
| AgentAuditDashboardAction { hubId :: !(Id Hub) }
|
||||||
|
| AdapterCompatibilityDashboardAction { hubId :: !(Id Hub) }
|
||||||
deriving (Eq, Show, Data)
|
deriving (Eq, Show, Data)
|
||||||
|
|
||||||
data WidgetsController
|
data WidgetsController
|
||||||
@@ -111,6 +112,25 @@ data AgentProposalsController
|
|||||||
| RejectProposalAction { agentProposalId :: !(Id AgentProposal) }
|
| RejectProposalAction { agentProposalId :: !(Id AgentProposal) }
|
||||||
deriving (Eq, Show, Data)
|
deriving (Eq, Show, Data)
|
||||||
|
|
||||||
|
data EnvelopeEmissionContractsController
|
||||||
|
= EnvelopeEmissionContractsAction
|
||||||
|
| ShowEnvelopeEmissionContractAction { envelopeEmissionContractId :: !(Id EnvelopeEmissionContract) }
|
||||||
|
deriving (Eq, Show, Data)
|
||||||
|
|
||||||
|
data InteractionReportingContractsController
|
||||||
|
= InteractionReportingContractsAction
|
||||||
|
| ShowInteractionReportingContractAction { interactionReportingContractId :: !(Id InteractionReportingContract) }
|
||||||
|
deriving (Eq, Show, Data)
|
||||||
|
|
||||||
|
data WidgetAdapterSpecsController
|
||||||
|
= WidgetAdapterSpecsAction
|
||||||
|
| ShowWidgetAdapterSpecAction { widgetAdapterSpecId :: !(Id WidgetAdapterSpec) }
|
||||||
|
| NewWidgetAdapterSpecAction
|
||||||
|
| CreateWidgetAdapterSpecAction
|
||||||
|
| EditWidgetAdapterSpecAction { widgetAdapterSpecId :: !(Id WidgetAdapterSpec) }
|
||||||
|
| UpdateWidgetAdapterSpecAction { widgetAdapterSpecId :: !(Id WidgetAdapterSpec) }
|
||||||
|
deriving (Eq, Show, Data)
|
||||||
|
|
||||||
data SessionsController
|
data SessionsController
|
||||||
= NewSessionAction
|
= NewSessionAction
|
||||||
| CreateSessionAction
|
| CreateSessionAction
|
||||||
|
|||||||
Reference in New Issue
Block a user