Phase 2 — Structured Feedback and Triage (IHUB-WP-0002): - Schema: annotation_threads, requirement_candidates, triage_states, reviewer_assignments; annotations extended with severity + thread_id - AnnotationThreadsController: create threads, assign annotations - RequirementCandidatesController: CRUD, escalation, triage lifecycle, reviewer assignment, my-queue - Annotation severity (low/medium/high/critical) with Tailwind color cues - TriageDashboardAction on HubsController with autoRefresh - Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md Phase 3 — Governance and Decision Linkage (IHUB-WP-0003): - Workplan registered: 9 tasks, State Hub workstream 5f201ee3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.4 KiB
IHF Phase 2 — Structured Feedback and Triage: Summary
What Was Built
Phase 2 transforms raw annotations into governable, triageable feedback. It delivers five capabilities on top of the Phase 1 interaction core.
1. Annotation Severity (T01, T02)
Every annotation now carries a severity value (low / medium / high / critical,
default medium). Severity is validated on create, displayed with Tailwind color cues
(gray / blue / yellow / red), and visible throughout annotation index, show, and thread views.
2. Annotation Threads (T03)
AnnotationThread groups related annotations under a named thread scoped to a widget.
Operators create threads manually, then assign individual annotations to them via
AssignAnnotationToThreadAction. Thread views show a severity distribution bar and
dominant category badge, giving a quick signal about the nature of the cluster.
Routes: /widgets/:widgetId/threads/
3. Requirement Candidates (T04, T05)
RequirementCandidate is the first artifact in the IHF traceability chain beyond raw
feedback. It can be created:
- Manually via
/requirement-candidates/new(operator selects widget, thread, category). - By escalation from an annotation detail page —
EscalateAnnotationActioncreates a candidate pre-populated from the annotation and linkssource_annotation_id. Escalation is idempotent: a second click redirects to the existing candidate.
Candidates are never deleted; they can only be rejected or deferred to preserve the audit trail.
4. Triage Lifecycle (T06)
TriageState records every status transition as an append-only audit log. Allowed transitions:
open → in_review
in_review → accepted | rejected | deferred
deferred → in_review
UpdateTriageStatusAction validates the transition and returns 422 on invalid moves.
The candidate's status column is kept in sync as a denormalised read-optimised cache;
triage_states is the authoritative history.
5. Reviewer Assignment & My Queue (T07)
ReviewerAssignment tracks one reviewer per candidate (unique constraint). Assignment is
an upsert (old record deleted, new inserted). MyQueueAction shows the current user's
assigned open/in-review candidates.
6. Triage Dashboard (T08)
TriageDashboardAction (on HubsController) is wrapped with autoRefresh do for live
push updates. It renders:
- KPI row — counts per status (open / in_review / accepted / rejected / deferred).
- Triage queue — open candidates for the hub's widgets, oldest first.
- Recent escalations — last 20 candidates across the hub.
- Category breakdown — annotation counts per category as a horizontal bar chart.
The {autoRefreshMeta} tag is emitted in the view head; morphdom.js handles DOM diffing
on each WebSocket push.
Files Created / Modified
| Path | Change |
|---|---|
Application/Schema.sql |
Added annotation_threads, severity/thread_id on annotations, requirement_candidates, triage_states, reviewer_assignments |
Application/Migration/1743120000-ihf-phase2-feedback-triage.sql |
Phase 2 migration |
Web/Types.hs |
Extended AnnotationsController; added AnnotationThreadsController, RequirementCandidatesController, TriageDashboardAction |
Web/Routes.hs |
Registered Phase 2 controllers |
Web/Controller/Annotations.hs |
Added ShowAnnotationAction, EscalateAnnotationAction, severity binding |
Web/Controller/AnnotationThreads.hs |
New |
Web/Controller/RequirementCandidates.hs |
New — full CRUD + triage + reviewer + my-queue |
Web/Controller/Hubs.hs |
Added TriageDashboardAction |
Web/View/Annotations/New.hs |
Added severity select |
Web/View/Annotations/Index.hs |
Severity badges, Escalate/Details link |
Web/View/Annotations/Show.hs |
New — annotation detail + escalation UI |
Web/View/AnnotationThreads/{Index,New,Show}.hs |
New |
Web/View/RequirementCandidates/{Index,New,Edit,Show}.hs |
New |
Web/View/Hubs/Show.hs |
Triage Dashboard link |
Web/View/Hubs/TriageDashboard.hs |
New |
Test/Integration.hs |
Phase 2 integration tests |
SCOPE.md |
Updated current state to Phase 2 complete |
Known Limitations
- No HTMX partial swaps — triage and escalation actions use full redirects rather than
HTMX in-place swaps. The architecture supports HTMX (types are
data-driven), but the Phase 2 target was to deliver correct behaviour; HTMX enhancement is Phase 3 polish. - No duplicate-detection heuristics — annotation threading is fully operator-driven. Automated similarity/clustering is Phase 3+ scope per the workplan notes.
- My Queue is not linked from the nav — accessible via
/my-queue. A nav link should be added in a follow-up. - TriageState is never cleaned up — by design (append-only audit trail). No archival policy is defined yet.
Phase 3 Readiness
Phase 2 exit criteria are met:
- Feedback volume can be triaged rather than merely stored
- Multiple related comments can converge into a structured candidate
- Reviewers can track status and ownership
Phase 3 (Decision Records) can begin. Prerequisites already in place:
RequirementCandidatewithacceptedstatus is the input artifact.source_widget_id,source_thread_id,source_annotation_idprovide full traceability back.triage_statesaudit trail establishes provenance for any futureDecisionRecord.