Files
inter-hub/workplans/IHUB-WP-0002-ihf-phase2-structured-feedback-and-triage.md
Bernd Worsch 87049dfc1f
Some checks failed
Test / test (push) Has been cancelled
chore: register Phase 2 workplan (IHUB-WP-0002)
IHF Phase 2 — Structured Feedback and Triage. 9 tasks (T01–T09)
covering AnnotationThread, RequirementCandidate, TriageState,
ReviewerAssignment, escalation action, and triage dashboard.
Workstream registered in State Hub under inter_hub topic.

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

12 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-0002 workplan IHF Phase 2 — Structured Feedback and Triage inter_hub inter-hub active custodian inter_hub 2026-03-28 2026-03-28 25d4c92c-b213-4c33-9404-822192899de7

IHF Phase 2 — Structured Feedback and Triage

Goal

Transform raw annotations into structured, operable feedback. Phase 1 established the capture layer (widgets, events, annotations). Phase 2 makes that feedback governable: grouping related observations, escalating them into Requirement Candidates, tracking triage status and reviewer ownership, and surfacing the triage queue to operators in a live dashboard.

Background

Phase 1 (IHUB-WP-0001) delivered the Minimal Interaction Core — widget registry, interaction event capture, annotation system, and hub operator dashboard. All Phase 1 exit criteria are met.

Phase 2 is the second of eight phases in the IHF specification (specs/InteractionHubFrameworkSpecification_v0.1.md). It extends the Phase 1 annotation model with severity markers, threading/grouping, escalation to Requirement Candidates, a triage lifecycle, and reviewer assignment.

Technology stack: IHP v1.5 (Haskell, Nix), PostgreSQL, AutoRefresh (triage dashboard), HTMX (escalation and triage actions), IHP forms (CRUD).

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

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

  • Feedback volume can be triaged rather than merely stored
  • Multiple related comments can converge into a structured candidate
  • Reviewers can track status and ownership

Data Artifacts Introduced (Phase 2)

AnnotationThread, RequirementCandidate, TriageState, ReviewerAssignment

Also extends: Annotation (adds severity, thread_id)


Tasks

T01 — Schema: AnnotationThread, RequirementCandidate, TriageState, ReviewerAssignment

id: IHUB-WP-0002-T01
status: todo
priority: high
state_hub_task_id: "eb267a9e-7e80-4913-b7a3-7f5adb04a0f2"

Add Phase 2 tables to Application/Schema.sql and write migration:

-- Extend annotations with severity and thread grouping
ALTER TABLE annotations ADD COLUMN severity TEXT NOT NULL DEFAULT 'medium';
ALTER TABLE annotations ADD COLUMN thread_id UUID REFERENCES annotation_threads(id) ON DELETE SET NULL;

CREATE TABLE annotation_threads (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE,
    title TEXT NOT NULL,
    description TEXT,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE TABLE requirement_candidates (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    title TEXT NOT NULL,
    description TEXT NOT NULL,
    source_widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE RESTRICT,
    source_thread_id UUID REFERENCES annotation_threads(id) ON DELETE SET NULL,
    source_annotation_id UUID REFERENCES annotations(id) ON DELETE SET NULL,
    category TEXT NOT NULL DEFAULT 'friction',
    status TEXT NOT NULL DEFAULT 'open',
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX requirement_candidates_widget_id_idx ON requirement_candidates (source_widget_id);
CREATE INDEX requirement_candidates_status_idx ON requirement_candidates (status);

CREATE TABLE triage_states (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE CASCADE,
    status TEXT NOT NULL,
    notes TEXT,
    changed_by UUID REFERENCES users(id),
    changed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

CREATE INDEX triage_states_candidate_id_idx ON triage_states (candidate_id);

CREATE TABLE reviewer_assignments (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    assigned_by UUID REFERENCES users(id),
    assigned_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
    UNIQUE (candidate_id)
);
  • Valid severity values: low, medium, high, critical
  • Valid requirement_candidates.status values: open, in_review, accepted, rejected, deferred
  • Verify Haskell types are generated correctly

Exit criteria: migrate runs cleanly; all Phase 2 types available in GHCi.


T02 — Severity markers on Annotation

id: IHUB-WP-0002-T02
status: todo
priority: high
state_hub_task_id: "fdcbf823-484e-4f0f-a0ca-28f9222520af"
  1. Update CreateAnnotationAction to bind and validate severity (low/medium/high/critical, default medium)
  2. Update annotation form: add severity select field
  3. Update annotation list and show views to display severity with Tailwind color cues:
    • low → muted/secondary
    • medium → info/neutral
    • high → warning
    • critical → error/destructive (per specs/TailwindForInteractionHubs_v0.2.md color roles)

Exit criteria: New annotations carry severity; existing annotations default to medium; severity is visible in all annotation views.


T03 — AnnotationThread controller (duplicate grouping)

id: IHUB-WP-0002-T03
status: todo
priority: high
state_hub_task_id: "35b989a0-5e2a-4300-990b-f43d67de0727"
  1. Scaffold AnnotationThreadsController scoped to a widget: /widgets/:widgetId/threads/
  2. Actions: IndexAnnotationThreadsAction, ShowAnnotationThreadAction, NewAnnotationThreadAction, CreateAnnotationThreadAction
  3. CreateAnnotationThreadAction: creates thread with title, description, widgetId
  4. Add AssignAnnotationToThreadAction (HTMX POST): sets annotation.thread_id
  5. Thread show view: list of member annotations with severity distribution bar and dominant category badge
  6. Thread list view: thread title, annotation count, severity breakdown, created_at

Exit criteria: Operator can create a thread, assign annotations to it, and view the grouped observations.


T04 — RequirementCandidate controller and views

id: IHUB-WP-0002-T04
status: todo
priority: high
state_hub_task_id: "4eb2a51c-1b3f-4b36-b945-6bfb14c2e680"
  1. Scaffold RequirementCandidatesController
  2. Actions: index, show, new, create, edit, update (no delete)
  3. Fields: title, description, sourceWidgetId (select), sourceThreadId (optional select), category (select), initial status open
  4. Index view: table with status badge, widget, category, reviewer name, created_at; filterable by status
  5. Show view: full detail + linked source (annotation or thread) + triage history log + current reviewer

Exit criteria: Requirement candidates can be created manually, listed, filtered by status, and viewed with full context.


T05 — Escalation action: Annotation(s) → RequirementCandidate

id: IHUB-WP-0002-T05
status: todo
priority: high
state_hub_task_id: "5c3a154b-38e0-4e40-9e97-57aae1dbc95d"
  1. Add "Escalate to Candidate" button on annotation show page (HTMX POST to EscalateAnnotationAction { annotationId })
  2. Action pre-populates and creates a RequirementCandidate with:
    • title derived from annotation body (first 80 chars)
    • category copied from annotation category
    • sourceAnnotationId set
    • sourceWidgetId from annotation's widget
  3. Respond with HTMX swap: replace escalate button with "Escalated →" link to the candidate
  4. Add visual indicator on escalated annotations in annotation lists

Exit criteria: Single-click escalation creates a linked candidate; escalated annotations are visually distinct; no duplicate escalation possible (button replaced after action).


T06 — TriageState lifecycle per RequirementCandidate

id: IHUB-WP-0002-T06
status: todo
priority: high
state_hub_task_id: "cd8c3ef1-e0f7-435f-ae20-e0760df5da83"
  1. UpdateTriageStatusAction { candidateId, newStatus, notes } (HTMX POST)
  2. Validate allowed transitions:
    • openin_review
    • in_reviewaccepted, rejected, deferred
    • deferredin_review
    • All others rejected with 422
  3. On valid transition: insert TriageState row, update requirement_candidates.status
  4. Show page: render full triage history as an audit trail (status, changed_by, changed_at, notes)
  5. Status badge in index updates live via AutoRefresh

Exit criteria: Triage lifecycle enforced; invalid transitions rejected; full audit trail visible on show page.


T07 — ReviewerAssignment: ownership on RequirementCandidate

id: IHUB-WP-0002-T07
status: todo
priority: medium
state_hub_task_id: "3dc9bfdb-06d0-48a5-8973-2e39c6e0f78a"
  1. AssignReviewerAction { candidateId, userId } (HTMX POST from candidate show page)
  2. Upsert ReviewerAssignment (one reviewer per candidate; replacing previous assignment)
  3. Show current reviewer name on candidate index and show pages
  4. Add "My Queue" view: MyQueueAction — lists all open/in_review candidates assigned to currentUser
  5. Index view: filter by "Unassigned" and "Assigned to me"

Exit criteria: Candidates can be assigned to reviewers; reviewer sees their queue; unassigned candidates are identifiable.


T08 — Operator triage dashboard (AutoRefresh)

id: IHUB-WP-0002-T08
status: todo
priority: high
state_hub_task_id: "82498422-1626-4479-9daa-3d7c7e088d8e"
  1. Add TriageDashboardAction (or extend ShowHubAction) wrapped with autoRefresh do
  2. Dashboard panels:
    • KPI row: open / in_review / accepted / rejected / deferred counts
    • Triage queue: open candidates sorted oldest-first (title, widget, category, age)
    • Recent escalations: last 20 with widget and source annotation snippet
    • Category breakdown: annotation count grouped by category across all hub widgets
  3. Layout includes {autoRefreshMeta}, morphdom.js, ihp-auto-refresh.js
  4. Test: insert a new candidate via the UI → dashboard updates within ~1s without reload

Exit criteria: Dashboard live-updates on candidate/triage changes. WebSocket frames visible in DevTools.


T09 — Phase 2 gate: tests, consistency, docs

id: IHUB-WP-0002-T09
status: todo
priority: high
state_hub_task_id: "935de4d7-867f-49aa-bddf-6ff9435215de"
  1. Integration tests (Test/):
    • AnnotationThread create + assign annotation
    • Escalation: annotation → candidate, duplicate escalation blocked
    • Triage transitions: valid path (open → in_review → accepted); invalid transition rejected (422)
    • ReviewerAssignment: assign, reassign, my-queue filter
    • Triage dashboard: autoRefresh wrapper present
  2. Consistency sync:
    cd ~/the-custodian && make fix-consistency REPO=inter-hub
    
  3. Documentation updates:
    • Update SCOPE.md current state section: Phase 2 complete
    • Write docs/phase2-summary.md: what was built, known limitations, Phase 3 readiness
  4. Smoke test checklist:
    • devenv up → clean start
    • Annotate a widget, set severity high
    • Create a thread, assign two annotations to it
    • Escalate an annotation to a candidate
    • Run triage: open → in_review → accepted
    • Assign reviewer, check My Queue
    • Confirm triage dashboard auto-updates

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


Phase 2 Dependencies

  • Phase 1 schema stable (T01 depends on widgets, annotations, users from Phase 1)
  • annotation_threads before annotations FK extension (T01 ordering: create thread table before altering annotations)
  • Schema (T01) before all controller work (T02T08)
  • RequirementCandidate (T04) before Escalation (T05), TriageState (T06), ReviewerAssignment (T07)
  • All feature tasks (T01T08) before gate (T09)

Notes

  • Severity on Annotation is a Phase 1 schema extension, not a new table — it's a simple migration with a safe default.
  • No automated duplicate detection in Phase 2. Grouping is operator-driven via AnnotationThread. Automated similarity is Phase 3+ scope.
  • TriageState is append-only. Like InteractionEvent, never update/delete triage rows — only insert new status transitions.
  • HTMX for in-place actions (escalate, triage status, assign reviewer) keeps the operator flow fluid without full page reloads.
  • No RequirementCandidate delete. Candidates can be rejected or archived, never deleted, to preserve audit trail.