Files
inter-hub/workplans/IHUB-WP-0001-ihf-phase1-minimal-interaction-core.md
tegwick 8b6ce5bbc8 docs: add specification, reference docs, workplan, and agent guidance
Adds all Phase 0 content that was created but never committed:
- CLAUDE.md and SCOPE.md — agent and developer orientation
- specs/TailwindForInteractionHubs_v0.2.md — IHF Tailwind coding guide
- docs/ — five IHP v1.5 reference guides (overview, data, controllers, realtime, ihf-mapping)
- workplans/IHUB-WP-0001 — Phase 1 implementation plan (12 tasks, state-hub synced)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 02:07:13 +01:00

15 KiB
Raw Blame History

id, type, title, domain, repo, status, owner, topic_slug, created, updated, state_hub_workstream_id
id type title domain repo status owner topic_slug created updated state_hub_workstream_id
IHUB-WP-0001 workplan IHF Phase 1 — Minimal Interaction Core custodian inter-hub active custodian custodian 2026-03-27 2026-03-27 4733dbde-bdcf-4e00-b9b8-749f92e50cae

IHF Phase 1 — Minimal Interaction Core

Goal

Implement the minimum viable governed interaction substrate for the Interaction Hub Framework: a working widget registry, interaction event capture, annotation system, and hub-level operator dashboard. This delivers Phase 1 of the IHF specification (specs/InteractionHubFrameworkSpecification_v0.1.md).

Background

Phase 0 (specification foundation) is complete. The IHF spec defines 8 phases; Phase 1 establishes the semantic core that all subsequent phases build on.

Technology stack: IHP v1.5 (Haskell, Nix), PostgreSQL, AutoRefresh (live dashboards), HTMX (governance actions), standard IHP forms (widget/annotation CRUD).

Reference: docs/ihp-overview.md, docs/ihp-data-and-queries.md, docs/ihp-controllers-views-forms.md, docs/ihp-realtime.md, docs/ihp-ihf-mapping.md.

Phase 1 Exit Criteria (from IHF spec §14 Phase 1)

  • Widgets can be addressed and commented on reliably
  • Interaction data is captured with actor attribution and view context
  • Hub-level inspection of interaction signals is possible

Data Artifacts Introduced (Phase 1)

Hub, Widget, WidgetVersion, InteractionEvent, Annotation, CapabilityReference, ViewContext


Tasks

T01 — IHP project bootstrap

id: IHUB-WP-0001-T01
status: todo
priority: high
state_hub_task_id: "e9e83628-d485-4163-9467-0d161f6274f3"

Set up the IHP project skeleton for inter-hub:

  1. Install Determinate Nix and ihp-new if not already present
  2. Run ihp-new ihf inside /home/worsch/inter-hub/ (or initialise in-place)
  3. Verify devenv up starts cleanly (app on :8000, IDE on :8001, Postgres managed by Nix)
  4. Commit the baseline scaffold
  5. Note first-startup time (expect 1015 min for Nix cache population)

Exit criteria: devenv up succeeds; http://localhost:8000 returns the IHP welcome page.


T02 — Schema design: Hub, Widget, WidgetVersion

id: IHUB-WP-0001-T02
status: todo
priority: high
state_hub_task_id: "e7254445-1375-44c3-9c59-111215b70692"

Define the widget registry tables in Application/Schema.sql:

CREATE TABLE hubs (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    slug TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    domain TEXT NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE TABLE widgets (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE RESTRICT,
    name TEXT NOT NULL,
    widget_type TEXT NOT NULL,
    capability_ref TEXT,
    view_context TEXT,
    policy_scope TEXT NOT NULL DEFAULT 'internal',
    status TEXT NOT NULL DEFAULT 'active',
    version INTEGER NOT NULL DEFAULT 1,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE TABLE widget_versions (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    version INTEGER NOT NULL,
    schema_snapshot JSONB NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (widget_id, version)
);
  • Write corresponding migration file in Application/Migration/
  • Verify Haskell types are generated correctly (IHP auto-generates on save)
  • Seed a dev Hub record for local development

Exit criteria: migrate runs cleanly; Hub, Widget, WidgetVersion types available in GHCi.


T03 — Schema design: InteractionEvent and Annotation

id: IHUB-WP-0001-T03
status: todo
priority: high
state_hub_task_id: "dac18955-7b2f-464f-97eb-0733c9163088"

Define the capture tables in Application/Schema.sql:

CREATE TABLE interaction_events (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    event_type TEXT NOT NULL,
    actor_id UUID,
    actor_type TEXT NOT NULL DEFAULT 'user',
    view_context_ref TEXT,
    metadata JSONB DEFAULT '{}' NOT NULL,
    occurred_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX interaction_events_widget_id_idx ON interaction_events (widget_id);
CREATE INDEX interaction_events_occurred_at_idx ON interaction_events (occurred_at DESC);

CREATE TABLE annotations (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    parent_id UUID REFERENCES annotations(id) ON DELETE CASCADE,
    body TEXT NOT NULL,
    category TEXT NOT NULL DEFAULT 'friction',
    actor_id UUID,
    actor_type TEXT NOT NULL DEFAULT 'user',
    widget_state_ref TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX annotations_widget_id_idx ON annotations (widget_id);
  • Write migration file
  • interaction_events is append-only: add a PostgreSQL trigger or application-level guard preventing UPDATE/DELETE
  • Valid category values: friction, defect, wish, policy_concern, doc_gap, trust, other
  • Valid actor_type values: user, agent, automation, anonymous

Exit criteria: Migration runs cleanly; types generated; append-only guard in place.


T04 — Hub controller and views (CRUD)

id: IHUB-WP-0001-T04
status: todo
priority: high
state_hub_task_id: "20517418-85c9-4335-a445-dbbf99a81ae5"

Scaffold Hub management:

  1. Use IHP Code Generator (localhost:8001/Generators) to scaffold HubsController
  2. Implement index, show, new, create, edit, update, delete actions
  3. Index view: list of hubs with slug, domain, widget count
  4. Show view: hub details + list of widgets (with event counts)

Exit criteria: Hubs can be created, listed, viewed, edited, and deleted via the web UI.


T05 — Widget Registry controller and views (CRUD)

id: IHUB-WP-0001-T05
status: todo
priority: high
state_hub_task_id: "262bfdb0-896c-4873-981f-36ea865b5dfe"

Scaffold Widget management:

  1. Scaffold WidgetsController
  2. Implement index, show, new, create, edit, update actions (no delete — widgets are deprecated, not deleted)
  3. CreateWidgetAction: on create, also insert a WidgetVersion record with version=1 and a JSON snapshot of the widget
  4. UpdateWidgetAction: increment version, insert new WidgetVersion record
  5. Index view: table of widgets with hub, type, status, version, event count
  6. Show view: widget detail + version history + recent interaction events + annotations
  7. Form: name, widget_type (select), hubId (select), capabilityRef, viewContext, policyScope (select: internal/hub/public), status

Exit criteria: Widgets can be registered, listed, and viewed. Version history is tracked on every update.


T06 — Interaction Event capture

id: IHUB-WP-0001-T06
status: todo
priority: high
state_hub_task_id: "3a48509e-9014-43d1-a244-21d7c322d8cc"

Implement interaction event capture:

  1. POST /widgets/:widgetId/eventsCreateInteractionEventAction { widgetId }
  2. Bind: event_type, actor_id (optional), actor_type, view_context_ref, metadata (JSON)
  3. Validate: event_type must be non-empty and in the canonical list (viewed, clicked, submitted, abandoned, retried, failed, commented, flagged_confusing, flagged_helpful, blocked_by_policy, escalated, accepted_recommendation, rejected_recommendation)
  4. Populate actor_id / actor_type from currentUserOrNothing when the actor is authenticated
  5. Respond with JSON { id, widget_id, event_type, occurred_at } for programmatic clients
  6. No HTML view needed for this action — it's a capture endpoint

Exit criteria: POST to the capture endpoint creates an InteractionEvent record with correct actor attribution; unknown event_type values are rejected with 422.


T07 — Annotation controller

id: IHUB-WP-0001-T07
status: todo
priority: high
state_hub_task_id: "1cc61933-46cd-46d1-b79a-05a8b40cd23b"

Implement annotation CRUD:

  1. Scaffold AnnotationsController scoped to a widget: /widgets/:widgetId/annotations/
  2. IndexAnnotationsAction { widgetId } — list annotations, threaded by parent_id
  3. CreateAnnotationAction { widgetId } — create annotation, auto-set actor_id/actor_type from session
  4. Form: body (textarea), category (select), optional parentId (for replies), widgetStateRef
  5. Validate: body non-empty; category in valid set
  6. List view: threaded annotation tree (root annotations + replies indented)
  7. No edit/delete (append-only); add a "retract" flag if needed (retracted_at TIMESTAMP)

Exit criteria: Annotations can be created and listed per widget with threading. Actor attribution is automatic for logged-in users.


T08 — Widget Envelope convention

id: IHUB-WP-0001-T08
status: todo
priority: medium
state_hub_task_id: "d2dfbdf6-fe66-4478-afeb-7ea3f05bea2b"

Establish the Widget Envelope as a reusable HSX helper:

  1. Create Application/Helper/View.hs function widgetEnvelope:
widgetEnvelope :: Widget -> Html -> Html
widgetEnvelope widget inner = [hsx|
    <div
        class="ihf-widget"
        data-widget-id={tshow widget.id}
        data-widget-type={widget.widgetType}
        data-hub-id={tshow widget.hubId}
        data-capability-ref={fromMaybe "" widget.capabilityRef}
        data-view-context={fromMaybe "" widget.viewContext}
        data-policy-scope={widget.policyScope}
        data-widget-version={tshow widget.version}
    >
        {inner}
        <div class="ihf-widget-controls">
            <a href={pathTo WidgetAnnotationsAction { widgetId = widget.id }}
               class="ihf-annotate-btn">Annotate</a>
        </div>
    </div>
|]
  1. Document the convention in docs/widget-envelope-convention.md
  2. Demonstrate use in the Hub dashboard view by wrapping at least one widget card

Exit criteria: widgetEnvelope renders the correct data-* attributes; the annotate link is functional.


T09 — Hub operator dashboard (AutoRefresh)

id: IHUB-WP-0001-T09
status: todo
priority: high
state_hub_task_id: "b0ca9f93-cd64-421f-a426-999f35db148f"

Implement the live hub operator dashboard:

  1. ShowHubAction wrapped with autoRefresh do
  2. Dashboard shows:
    • Widget count by type and status
    • Recent interaction events (last 50, across all hub widgets)
    • Recent annotations (last 20, across all hub widgets)
    • Per-widget event count bar (simple table or list)
  3. Layout must include {autoRefreshMeta}, morphdom.js, ihp-auto-refresh.js
  4. Test: open dashboard in two browser tabs; insert an event via curl → both tabs update within ~1s

Exit criteria: Dashboard auto-updates on new events/annotations without page reload. AutoRefresh diff is confirmed in browser DevTools (WebSocket frames visible).


T10 — Authentication and actor attribution

id: IHUB-WP-0001-T10
status: todo
priority: medium
state_hub_task_id: "8ef87232-cb0d-4948-9bca-849048dd82c2"

Wire up IHP session auth for the admin/governance users:

  1. Add users table to Schema.sql: id, email, password_hash, locked_at, failed_login_attempts, name
  2. Configure initAuthentication @User in FrontController
  3. Mount SessionsController
  4. Add beforeAction = ensureIsUser to HubsController and WidgetsController
  5. Update CreateInteractionEventAction and CreateAnnotationAction to read currentUserOrNothing and set actor_id/actor_type accordingly
  6. Seed one admin user for local development (use hash-password CLI)

Exit criteria: Unauthenticated access to hubs/widgets redirects to login. Annotations and events created by logged-in users carry the correct actor_id.


T11 — Manual traceability view: Widget → Annotations

id: IHUB-WP-0001-T11
status: todo
priority: medium
state_hub_task_id: "b342d44c-ca41-4373-a55d-c7dcc5121f4a"

Implement the traceability entry point (first link in the IHF traceability chain):

  1. Widget show page (ShowWidgetAction) aggregates:
    • Full annotation thread (threaded, with actor, category, timestamp)
    • Interaction event history (paginated, 20 per page)
    • Widget version history
  2. Add a summary KPI row: total events, total annotations, annotation breakdown by category
  3. Link to parent hub from widget detail (breadcrumb: Hub > Widget)

This is the Phase 1 terminal traceability view: Widget → InteractionEvents + Annotations.

Exit criteria: The widget show page presents a complete picture of all interaction signals and annotations for a widget, linked back to the hub.


T12 — Phase 1 gate: tests, consistency, and documentation

id: IHUB-WP-0001-T12
status: todo
priority: high
state_hub_task_id: "ae5a8713-27ba-445b-a29f-822b5d0acf5a"

Gate tasks before Phase 1 is marked complete:

  1. Integration tests (Test/):
    • Widget CRUD happy path
    • Event capture with and without authenticated user
    • Annotation create + list + threading
    • Validation rejection (empty body, invalid category, invalid event_type)
    • AutoRefresh: verify autoRefresh wrapper is present on dashboard action
  2. Consistency sync:
    cd ~/the-custodian && make fix-consistency REPO=inter-hub
    
  3. Documentation updates:
    • Update SCOPE.md current state section: Phase 1 complete
    • Write brief docs/phase1-summary.md: what was built, known limitations, Phase 2 readiness
  4. Smoke test checklist:
    • devenv up → clean start
    • Create a hub, create 3 widgets, capture events via API, annotate via UI
    • Dashboard auto-updates visible
    • All tests pass

Exit criteria: All tests pass; consistency sync reports no errors; smoke test completed.


Phase 1 Dependencies

  • IHP v1.5 installed via Nix (T01)
  • Schema stabilized before controller scaffolding (T02/T03 before T04T07)
  • Auth before traceability view (T10 before T11)
  • All feature tasks (T01T11) before gate (T12)

Notes

  • No DataSync in Phase 1. AutoRefresh is sufficient for the operator dashboard. DataSync (with RLS) is Phase 2 work for widget embeds.
  • No requirement candidates or decision records in Phase 1. Those are Phase 2 (Structured Feedback and Triage) and Phase 3 (Governance and Decision Linkage).
  • Append-only events: the PostgreSQL trigger on interaction_events (T03) is critical — enforce it before wiring the capture endpoint.
  • IHP Code Generator: use it aggressively for T04T07 scaffolding, then customize. It handles the Types.hs / Routes.hs / FrontController.hs wiring automatically.