ADR-0028: Event-Driven Report Pipeline with Extractable Widget¶
Status¶
Accepted
Date: 2026-03-28
Enforcement: enforced
Enforced by: pytest:tests/test_report_event_flow.py
Context¶
The bug report pipeline (ReportIssueLoop) silently updated TrackedReport status on disk but never notified the frontend. Users submitted bugs and saw "queued" indefinitely until the next 30-second HTTP poll. The BugReportTracker was a modal with no filtering, sorting, or inline submission — unusable for power users.
Additionally, the report UI needed to be extractable as a standalone widget for embedding in external systems (other dashboards, Slack bots, monitoring tools) without depending on the HydraFlow React context.
Decision¶
Backend: Publish REPORT_UPDATE events at every status transition¶
Added EventType.REPORT_UPDATE to the EventBus. ReportIssueLoop._do_work() now publishes events at 6 transition points: queued→in-progress, filed, retry, escalation, stale close, and fixed. The frontend reducer handles report_update WebSocket events to update trackedReports inline.
Why events over direct state mutation: The EventBus is the established notification mechanism. WebSocket subscribers receive events automatically. Metrics and logging can also react. This is consistent with how WORKER_UPDATE, MERGE_UPDATE, etc. already work.
Frontend: Self-contained useReportPoller hook + BugReportPanel component¶
Replaced the BugReportTracker modal with a full-width BugReportPanel dashboard tab. The panel uses useReportPoller(apiBaseUrl, reporterId, { interval }) — a hook that manages its own polling, submission, and action dispatch.
Why HTTP polling over WebSocket-only: The widget must work outside the HydraFlow dashboard where no WebSocket connection exists. HTTP polling at 10s intervals is portable — any system with HTTP access can embed it. The WebSocket report_update handler provides instant updates when available, and the poll catches up when it's not.
Why a hook, not context: useReportPoller takes apiBaseUrl and reporterId as explicit props. No HydraFlowProvider needed. This makes the component truly extractable:
<BugReportPanel apiBaseUrl="https://hydra.example.com" reporterId={userId} />
Server: Status filter on GET /api/reports¶
Added ?status= query parameter to GET /api/reports for server-side filtering. The useReportPoller can request only active reports, reducing payload size for embedded widgets.
Consequences¶
- Report status updates are live via WebSocket when available, polled via HTTP otherwise
- The
BugReportPanelcan be extracted to a standalone npm package with zero HydraFlow dependencies - The old
BugReportTrackermodal and its tests were deleted REPORT_UPDATEevents appear in the WebSocket stream alongside all other events — external monitoring tools can subscribe