generated from coulomb/repo-seed
feat(P7): IHF Phase 7 complete — advanced observability and operational integration
Some checks failed
Test / test (push) Has been cancelled
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:
@@ -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 6 — Cross-Framework UI Adaptation Layer
|
||||
**File:** `workplans/IHUB-WP-0006-ihf-phase6-cross-framework-ui-adaptation.md`
|
||||
## Workplan: IHUB-WP-0007 — IHF Phase 7 — Advanced Observability and Operational Integration
|
||||
**File:** `workplans/IHUB-WP-0007-ihf-phase7-advanced-observability-and-operational-integration.md`
|
||||
|
||||
|
||||
# IHF Phase 6 — Cross-Framework UI Adaptation Layer
|
||||
# IHF Phase 7 — Advanced 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 1–5 are complete. The IHF core (widget registry, interaction events,
|
||||
annotations, requirements, decisions, outcomes, agent assistance) is stable.
|
||||
Phases 1–6 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,
|
||||
-- 0–100; 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,
|
||||
-- 0–100
|
||||
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:
|
||||
- 0–19: green (`bg-green-100`)
|
||||
- 20–39: yellow (`bg-yellow-100`)
|
||||
- 40–59: 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 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)
|
||||
- Phases 1–6 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 (T02–T05 before T06)
|
||||
- All feature tasks (T01–T06) 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
|
||||
|
||||
Reference in New Issue
Block a user