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>
This commit is contained in:
2026-03-27 02:07:13 +01:00
parent 75b88ee760
commit 8b6ce5bbc8
9 changed files with 2181 additions and 0 deletions

View File

@@ -0,0 +1,418 @@
---
id: IHUB-WP-0001
type: workplan
title: "IHF Phase 1 — Minimal Interaction Core"
domain: custodian
repo: inter-hub
status: active
owner: custodian
topic_slug: custodian
created: "2026-03-27"
updated: "2026-03-27"
state_hub_workstream_id: "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
```task
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
```task
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`:
```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
```task
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`:
```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)
```task
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)
```task
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
```task
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/events``CreateInteractionEventAction { 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
```task
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
```task
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`:
```haskell
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>
|]
```
2. Document the convention in `docs/widget-envelope-convention.md`
3. 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)
```task
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
```task
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
```task
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
```task
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:**
```bash
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.