feat(P7): IHF Phase 7 complete — advanced observability and operational integration
Some checks failed
Test / test (push) Has been cancelled

T01 schema: friction_scores, bottleneck_records, hub_health_snapshots,
cross_hub_propagations + migration 1743552000.

T02 Widget Pain Heatmap: computeFrictionScore (formula documented), RecomputeFriction
action, colour-coded grid view (green/yellow/amber/red).

T03 Workflow Bottleneck Analysis: detectBottlenecks across 4 pipeline stages
(candidate 30d, requirement 60d, decision 30d, observation 14d), idempotent,
severity from age ratio, resolve action.

T04 Hub Health Correlation: computeHubHealth (deduction table documented),
append-only HubHealthSnapshot, health history view, badge on hub Show page.

T05 Cross-Hub Propagation: annotation_cluster + widget_type_friction heuristics,
idempotent detection, acknowledge/resolve lifecycle.

T06 Operational Review Board: 4-panel AutoRefresh global dashboard — health matrix,
top-10 friction, bottleneck stage counts, open propagations.

T07 gate: 5 describe blocks in Test/Integration.hs; SCOPE.md updated Phase 7
complete; docs/phase7-summary.md written.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:49:22 +00:00
parent c0b4b984b0
commit 98fb159582
22 changed files with 1638 additions and 262 deletions

View File

@@ -4,14 +4,14 @@ 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_id: IHUB-WP-0007
workplan_file: workplans/IHUB-WP-0007-ihf-phase7-advanced-observability-and-operational-integration.md
started_at: "2026-03-29T21:41:13Z"
---
## Workplan Status Check — Do This First, Every Iteration
Read the workplan file at: `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md`
Read the workplan file at: `workplans/IHUB-WP-0007-ihf-phase7-advanced-observability-and-operational-integration.md`
Count the task blocks (fenced code blocks with language tag `task`):
- How many tasks exist in total?
@@ -25,357 +25,371 @@ Otherwise: continue with the implementation below.
---
## Workplan: IHUB-WP-0006 — IHF Phase 6Cross-Framework UI Adaptation Layer
**File:** `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md`
## Workplan: IHUB-WP-0007 — IHF Phase 7Advanced Observability and Operational Integration
**File:** `workplans/IHUB-WP-0007-ihf-phase7-advanced-observability-and-operational-integration.md`
# IHF Phase 6Cross-Framework UI Adaptation Layer
# IHF Phase 7Advanced Observability and Operational Integration
## 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.
Integrate interaction governance with broader operational intelligence. Phase 6
established cross-framework widget participation. Phase 7 turns the accumulated
interaction data into operational intelligence: friction heatmaps, pipeline
bottleneck detection, per-hub health scores, and cross-hub pattern propagation.
The capstone is an Operational Review Board dashboard that gives hub leaders a
unified view across all hubs.
## Background
Phases 15 are complete. The IHF core (widget registry, interaction events,
annotations, requirements, decisions, outcomes, agent assistance) is stable.
Phases 16 are complete. The IHF core (widget registry, interaction events,
annotations, requirements, decisions, outcomes, agent assistance,
cross-framework adapters) is stable and extensible.
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
The spec (§Phase 7) calls for:
- Hub health correlation
- Policy violation correlation
- Workflow bottleneck analysis
- Interaction pain heatmaps
- Queue and job linkage
- Cross-hub issue propagation analysis
Artifacts introduced: `WidgetAdapterSpec`, `InteractionReportingContract`,
`EnvelopeEmissionContract`.
Artifacts introduced: `FrictionScore`, `BottleneckRecord`, `HubHealthSnapshot`,
`CrossHubPropagation`.
Reference: `specs/InteractionHubFrameworkSpecification_v0.1.md` §Phase 6,
`docs/ihp-overview.md`, `docs/ihp-controllers-views-forms.md`.
Reference: `specs/InteractionHubFrameworkSpecification_v0.1.md` §Phase 7,
`docs/phase6-summary.md`, `docs/ihp-controllers-views-forms.md`.
## Phase 6 Exit Criteria (from IHF spec §Phase 6)
## Phase 7 Exit Criteria (from IHF spec §Phase 7)
- New UI technologies can participate without bypassing the IHF core
- Widget identity remains stable across frontend evolution
- Annotations and interaction events remain compatible
- Interaction data informs operational decision-making
- Hub leaders can inspect systemic friction patterns
- The platform supports cross-domain learning
## Data Artifacts Introduced (Phase 6)
## Data Artifacts Introduced (Phase 7)
`WidgetAdapterSpec`, `InteractionReportingContract`, `EnvelopeEmissionContract`
`FrictionScore`, `BottleneckRecord`, `HubHealthSnapshot`, `CrossHubPropagation`
## Tasks
### T01 — Schema: WidgetAdapterSpec, InteractionReportingContract, EnvelopeEmissionContract
### T01 — Schema: FrictionScore, BottleneckRecord, HubHealthSnapshot, CrossHubPropagation
```task
id: IHUB-WP-0006-T01
id: IHUB-WP-0007-T01
status: todo
priority: high
state_hub_task_id: "8d92f9d5-ec3c-4d9b-b16c-26f938a306e7"
state_hub_task_id: "86e31f8b-62a3-4176-9b10-2fe7a8dbcc23"
```
Add Phase 6 tables to `Application/Schema.sql` and write migration:
Add Phase 7 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 (
-- Aggregated pain score per widget, recomputed on demand or scheduled.
CREATE TABLE friction_scores (
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
widget_id UUID NOT NULL REFERENCES widgets(id),
score INTEGER NOT NULL DEFAULT 0,
-- 0100; higher = more friction
annotation_count INTEGER NOT NULL DEFAULT 0,
error_event_count INTEGER NOT NULL DEFAULT 0,
regression_flag BOOLEAN NOT NULL DEFAULT FALSE,
stale_candidate_count INTEGER NOT NULL DEFAULT 0,
last_computed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
UNIQUE (widget_id)
);
CREATE INDEX friction_scores_widget_id_idx ON friction_scores (widget_id);
CREATE INDEX friction_scores_score_idx ON friction_scores (score DESC);
-- Detected stalls at specific pipeline stages.
CREATE TABLE bottleneck_records (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
hub_id UUID NOT NULL REFERENCES hubs(id),
stage TEXT NOT NULL,
-- 'candidate' | 'requirement' | 'decision' | 'observation'
subject_type TEXT NOT NULL,
-- 'RequirementCandidate' | 'Requirement' | 'DecisionRecord' | 'DeploymentRecord'
subject_id UUID NOT NULL,
stalled_since TIMESTAMP WITH TIME ZONE NOT NULL,
severity TEXT NOT NULL DEFAULT 'medium',
-- 'low' | 'medium' | 'high' | 'critical'
resolved_at TIMESTAMP WITH TIME ZONE,
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 (
CREATE INDEX bottleneck_records_hub_id_idx ON bottleneck_records (hub_id);
CREATE INDEX bottleneck_records_stage_idx ON bottleneck_records (stage);
CREATE INDEX bottleneck_records_resolved_idx ON bottleneck_records (resolved_at)
WHERE resolved_at IS NULL;
-- Periodic health snapshots for trend tracking.
CREATE TABLE hub_health_snapshots (
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
hub_id UUID NOT NULL REFERENCES hubs(id),
health_score INTEGER NOT NULL,
-- 0100
open_candidates INTEGER NOT NULL DEFAULT 0,
regressed_widgets INTEGER NOT NULL DEFAULT 0,
stale_decisions INTEGER NOT NULL DEFAULT 0,
active_bottlenecks INTEGER NOT NULL DEFAULT 0,
computed_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 hub_health_snapshots_hub_id_idx ON hub_health_snapshots (hub_id);
CREATE INDEX hub_health_snapshots_computed_at_idx
ON hub_health_snapshots (hub_id, computed_at DESC);
CREATE INDEX widgets_adapter_spec_id_idx ON widgets (adapter_spec_id);
-- Patterns detected across multiple hubs.
CREATE TABLE cross_hub_propagations (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
pattern_type TEXT NOT NULL,
-- 'annotation_cluster' | 'widget_type_friction'
source_hub_id UUID REFERENCES hubs(id),
affected_hub_ids JSONB NOT NULL DEFAULT '[]',
-- array of hub UUIDs
summary TEXT NOT NULL,
detected_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
status TEXT NOT NULL DEFAULT 'open',
-- 'open' | 'acknowledged' | 'resolved'
notes TEXT
);
CREATE INDEX cross_hub_propagations_status_idx ON cross_hub_propagations (status);
CREATE INDEX cross_hub_propagations_pattern_idx ON cross_hub_propagations (pattern_type);
```
**Exit criteria:** `migrate` runs cleanly; all Phase 6 types available in GHCi.
**Exit criteria:** `migrate` runs cleanly; all Phase 7 types available in GHCi.
### T02 — EnvelopeEmissionContract: formalise widgetEnvelope as a versioned contract
### T02 — Widget Pain Heatmap: friction scoring and per-hub heatmap view
```task
id: IHUB-WP-0006-T02
id: IHUB-WP-0007-T02
status: todo
priority: high
state_hub_task_id: "298af675-550b-480b-bed6-05efc79cd0c9"
state_hub_task_id: "3a5ecd28-17c2-4258-bfd9-b3eaecf52135"
```
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"
1. Add `Application/Helper/FrictionScore.hs` with `computeFrictionScore`:
- `annotation_count` — total annotations for widget
- `error_event_count` — events with `event_type = 'errored'`
- `regression_flag``True` if widget appears in `regressedWidgetIds`
- `stale_candidate_count` — open candidates older than 30 days
- Score formula (documented in module header):
```
score = min 100 $
annotationCount * 5
+ errorEventCount * 10
+ (if regressionFlag then 20 else 0)
+ staleCandidateCount * 8
```
- Upserts into `friction_scores` (UPDATE if exists, INSERT otherwise)
2. Add `RecomputeFrictionAction { hubId }` to `HubsController`:
- Recomputes scores for all widgets in the hub
- Redirects back to heatmap view
3. Add `FrictionHeatmapAction { hubId }` view:
- Grid of widget cards, colour-coded by score band:
- 019: green (`bg-green-100`)
- 2039: yellow (`bg-yellow-100`)
- 4059: amber (`bg-orange-100`)
- 60+: red (`bg-red-100`)
- Each card: widget name, score, link to widget show
- "Recompute" button triggers `RecomputeFrictionAction`
4. Link from hub Show page as "Friction Heatmap"
**Exit criteria:** Active contract record exists in DB; widgetEnvelope validates
against it; contract index/show pages render correctly.
**Exit criteria:** Scores compute correctly for test fixtures; heatmap renders
with correct colour bands; recompute updates scores.
### T03 — InteractionReportingContract: REST endpoint for external event submission
### T03 — Workflow Bottleneck Analysis: stall detection across the pipeline
```task
id: IHUB-WP-0006-T03
id: IHUB-WP-0007-T03
status: todo
priority: high
state_hub_task_id: "f2767465-ff00-48be-b2dc-5bf3b179cca9"
state_hub_task_id: "ada0347a-880b-454e-843f-4a9135ea8739"
```
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)
1. Add `Application/Helper/BottleneckDetector.hs` with `detectBottlenecks`:
- Stage 1 — `candidate`: `RequirementCandidate` with `status='open'` and
`created_at < now() - interval '30 days'`
- Stage 2 — `requirement`: `Requirement` with no linked `DecisionRecord` and
`created_at < now() - interval '60 days'`
- Stage 3 — `decision`: `DecisionRecord` with no linked `DeploymentRecord`
and `decided_at < now() - interval '30 days'`
- Stage 4 — `observation`: `DeploymentRecord` with no linked `OutcomeSignal`
and `deployed_at < now() - interval '14 days'`
- Severity: `critical` if age > 2× threshold, else `high` if > 1.5×, else `medium`
- Upserts `BottleneckRecord` (skip if already exists for same subject)
2. Add `DetectBottlenecksAction { hubId }` — runs detector, redirects to dashboard
3. Add `BottleneckDashboardAction { hubId }` view:
- Table grouped by pipeline stage
- Columns: subject (linked), stalled since, age, severity badge
- "Resolve" button → `ResolveBottleneckAction { bottleneckRecordId }`
- "Detect" button triggers fresh detection
4. Link from hub Show page as "Bottlenecks"
**Exit criteria:** `POST /api/v1/interaction-events` with a valid payload creates
an `InteractionEvent`; invalid payloads return `422`; contract show page renders.
**Exit criteria:** Stale candidates create bottleneck records; dashboard renders
and groups correctly; resolve marks `resolved_at`.
### T04 — WidgetAdapterSpecsController and registry dashboard
### T04 — Hub Health Correlation: composite health score and history
```task
id: IHUB-WP-0006-T04
id: IHUB-WP-0007-T04
status: todo
priority: high
state_hub_task_id: "e84016d0-60c0-48cb-ad70-2c054d2530db"
state_hub_task_id: "b0c932c5-fdb7-47b6-adc7-b4f8ed5555e6"
```
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
1. Add `Application/Helper/HubHealth.hs` with `computeHubHealth`:
- Deduction table (documented in module):
```
-5 per open RequirementCandidate
-10 per regressed widget
-8 per stale DecisionRecord (decided > 30 days, no deployment)
-12 per active critical BottleneckRecord
-6 per active high BottleneckRecord
floor at 0
```
- Inserts new `HubHealthSnapshot` (never updates — history is append-only)
2. Add `SnapshotHubHealthAction { hubId }` — computes and redirects to history
3. Add `HubHealthHistoryAction { hubId }` view:
- Table of snapshots: timestamp, score (colour-coded), component breakdown
- Latest score shown prominently at top
4. Show health score badge on hub Show page (next to dashboard links):
- Fetch latest snapshot; display colour-coded score pill
- If no snapshot: "" with link to take first snapshot
**Exit criteria:** Adapter specs can be registered, listed, and viewed; widget
form allows adapter assignment; widget show page renders adapter badge.
**Exit criteria:** Snapshot computes correct score against test fixtures; history
table renders in order; badge appears on hub Show page.
### T05 — Cross-framework annotation launcher (lightweight JS widget)
### T05 — Cross-Hub Propagation Analysis: pattern detection across hubs
```task
id: IHUB-WP-0006-T05
id: IHUB-WP-0007-T05
status: todo
priority: medium
state_hub_task_id: "fea86955-d5e6-4623-b5cc-f422c266c9cf"
state_hub_task_id: "7a860b9f-a835-47d6-96d8-2964ae37b12d"
```
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`
1. Add `Application/Helper/CrossHubPropagation.hs` with `detectPropagations`:
- **Annotation cluster heuristic**: for each annotation `category`, count
distinct hubs with ≥3 annotations in that category in the last 14 days.
If ≥2 hubs qualify, emit a `CrossHubPropagation` with
`pattern_type='annotation_cluster'` and a generated summary.
- **Widget type friction heuristic**: for each `widget_type`, count hubs
where the max `FrictionScore` for that type is ≥40. If ≥2 hubs qualify,
emit `pattern_type='widget_type_friction'`.
- Skip if a matching open/acknowledged propagation already exists
(idempotent detection)
2. Add `DetectPropagationsAction` (global, no hubId) — runs detector
3. Add `CrossHubPropagationsAction` view (global):
- Table: pattern type, source hub, affected hubs (comma list), summary,
detected at, status badge
- "Acknowledge" and "Resolve" actions
4. Link from global nav (alongside "Adapters", "Ops Review")
**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).
**Exit criteria:** Detection creates propagation records for qualifying patterns;
duplicate runs are idempotent; acknowledge/resolve transitions work.
### T06 — React adapter specification and reference example
### T06 — Operational Review Board Dashboard: cross-hub unified view
```task
id: IHUB-WP-0006-T06
id: IHUB-WP-0007-T06
status: todo
priority: medium
state_hub_task_id: "023269d8-9835-40b4-a394-478a0f36eee0"
state_hub_task_id: "ffabc4d1-c166-4b7d-8bec-55365cbe0666"
```
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
1. Add `OperationalReviewBoardAction` to a new `OperationsController`
(or `HubsController` as a global action — no `hubId` parameter):
- **Panel 1 — Hub health matrix**: all hubs, latest health score (or ""),
colour-coded row, link to hub and to health history
- **Panel 2 — Top friction widgets**: top 10 across all hubs by
`FrictionScore.score DESC`; columns: widget name, hub, score band, link
- **Panel 3 — Active bottlenecks by stage**: count of unresolved bottlenecks
per stage across all hubs; click-through to hub bottleneck dashboard
- **Panel 4 — Open cross-hub propagations**: list of open/acknowledged
propagation events with pattern type and affected hub count
2. `autoRefresh` — live-updates
3. Link from global nav as "Ops Review"
4. Link from global nav cross-hub propagation count badge if > 0
**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.
**Exit criteria:** Dashboard renders all four panels; health matrix shows all
hubs; top friction list is correctly sorted; live-updates on data change.
### T07 — Adapter compatibility validation dashboard
### T07 — Phase 7 gate: tests, consistency, docs
```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
id: IHUB-WP-0007-T07
status: todo
priority: high
state_hub_task_id: "90ea4814-7603-4016-be34-d41ae091f7e1"
state_hub_task_id: "a14b94f8-3b27-4f0c-9949-60fb65a57a05"
```
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
- `FrictionScore` compute formula: widget with known annotation count →
expected score
- `BottleneckRecord` create + resolve: stale candidate → bottleneck detected;
resolve sets `resolved_at`
- `HubHealthSnapshot` compute: hub with known candidates/regressions → expected
score; history fetch returns in order
- `CrossHubPropagation` create + acknowledge + resolve
- `OperationalReviewBoard` action: compiles, fetches all hubs, returns 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
- Update `SCOPE.md` current state section: Phase 7 complete
- Write `docs/phase7-summary.md`: what was built, scoring formulae, bottleneck
thresholds, cross-hub heuristics, known limitations, Phase 8 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
- Create two hubs with widgets and annotations; run friction recompute; verify
heatmap colours
- Age a candidate by force-setting `created_at`; run detect bottlenecks;
verify record appears
- Snapshot health for both hubs; verify Ops Review Board health matrix
- Trigger cross-hub propagation detection; verify propagation record
- Open Ops Review Board; confirm all four panels populate
**Exit criteria:** All tests pass; consistency sync reports no errors; smoke
test completed; SCOPE.md updated.
## Phase 6 Dependencies
## Phase 7 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)
- Phases 16 schema stable (widget registry, interaction events, annotations,
requirements, decisions, outcomes, agent proposals, adapter specs)
- `friction_scores` requires widgets (T01 before T02)
- `bottleneck_records` requires hubs, candidates, requirements, decisions,
deployments (T01 before T03)
- `hub_health_snapshots` requires hubs and reads from bottleneck_records
(T03 before T04)
- `cross_hub_propagations` requires hub friction scores (T02 before T05)
- Operational Review Board aggregates all Phase 7 data (T02T05 before T06)
- All feature tasks (T01T06) before gate (T07)
## 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.
- **Friction scores are recomputed, not append-only.** Each widget has at most
one `FrictionScore` row (unique constraint on `widget_id`). Historical trend
is not tracked at the friction level — use `HubHealthSnapshot` for trends.
- **Bottleneck detection is idempotent.** Re-running the detector skips records
where an unresolved bottleneck already exists for the same subject.
- **Health snapshots are append-only.** Every `SnapshotHubHealthAction` call
inserts a new row. This preserves the health history for trend analysis.
- **Cross-hub detection requires FrictionScores to be current.** Run
`RecomputeFrictionAction` for all hubs before `DetectPropagationsAction`.
- **No scheduled jobs in Phase 7.** Detection and recomputation are triggered
manually via UI or curl. Phase 8 can layer on a cron/job system.
- **Severity thresholds and score weights are constants in the helper modules.**
They are intentionally not stored in the DB to avoid config drift — change
them in code and recompute.
---
@@ -408,7 +422,7 @@ status: active → status: done
Before marking the workplan done and outputting `<promise>HEUREKA</promise>`,
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`
1. Every task block in `workplans/IHUB-WP-0007-ihf-phase7-advanced-observability-and-operational-integration.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