# 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 — `EscalateAnnotationAction` creates a candidate pre-populated from the annotation and links `source_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: - [x] Feedback volume can be triaged rather than merely stored - [x] Multiple related comments can converge into a structured candidate - [x] Reviewers can track status and ownership Phase 3 (Decision Records) can begin. Prerequisites already in place: - `RequirementCandidate` with `accepted` status is the input artifact. - `source_widget_id`, `source_thread_id`, `source_annotation_id` provide full traceability back. - `triage_states` audit trail establishes provenance for any future `DecisionRecord`.