Quality Assurance - Issue Tracking & Notifications
User-facing QA issue reporting with admin triage, comment threads, email subscriptions, and domain event-driven notifications.
Components
| Component |
Location |
Purpose |
| backend-api |
backend-api/ |
NestJS service: reports CRUD, comments, subscriptions, notifications |
| frontend-widget |
frontend-widget/ |
Embeddable React widget for submitting bug reports from any page |
| frontend-admin |
frontend-admin/ |
Admin dashboard: triage, assign, comment, track reports |
| frontend-showcase |
frontend-showcase/ |
Demo/preview of the widget |
| shared |
shared/ |
Shared types, enums, DTOs (@lilith/qa-shared) |
Architecture
Reporter (any page) Admin Dashboard
│ │
│ Submit report │ Triage / Comment / Resolve
▼ ▼
┌──────────────────────────────────────────┐
│ QA Backend API (NestJS) │
│ │
│ Reports Comments Subscriptions │
│ Module Module Module │
│ │
│ Notifications Module │
│ (Domain Events Emitter) │
└──────────────┬───────────────────────────┘
│
│ Domain Events (BullMQ)
▼
┌──────────────────────────────────────────┐
│ Email Service - QA Events Processor │
│ │
│ qa:report_created → Confirmation + Alert│
│ qa:report_status_changed → Status email │
│ qa:report_comment_added → Reply notif. │
│ qa:report_resolved → Resolution email │
└──────────────────────────────────────────┘
Database Schema
qa_reports
| Column |
Type |
Notes |
| id |
uuid PK |
|
| title |
varchar(500) |
|
| description |
text |
|
| category |
enum |
bug, ui, performance, other |
| severity |
enum |
low, medium, high, critical |
| status |
enum |
new, triaged, in_progress, resolved, closed |
| page_url |
varchar(2048) |
URL where issue was reported |
| source_domain |
varchar(255) |
Domain (e.g. atlilith.com) |
| user_agent |
text |
Nullable |
| browser_name |
varchar(100) |
Nullable |
| browser_version |
varchar(50) |
Nullable |
| os_name |
varchar(100) |
Nullable |
| screen_resolution |
varchar(50) |
Nullable |
| reporter_email |
varchar(320) |
Nullable, for subscriptions |
| reporter_user_id |
uuid |
Nullable, if logged in |
| admin_notes |
text |
Nullable, legacy field |
| assigned_to |
varchar(255) |
Nullable |
| triaged_at |
timestamp |
Set when status → triaged |
| resolved_at |
timestamp |
Set when status → resolved |
| created_at |
timestamp |
Auto |
| updated_at |
timestamp |
Auto |
Indexes: status, severity, category, source_domain, created_at, reporter_email.
| Column |
Type |
Notes |
| id |
uuid PK |
|
| report_id |
uuid FK |
CASCADE delete |
| content |
text |
|
| visibility |
enum |
internal (admin-only) / reporter (emailed) |
| author_type |
enum |
admin, reporter, system |
| author_id |
varchar(255) |
Nullable, admin user ID |
| author_name |
varchar(255) |
Nullable, display name |
| author_email |
varchar(320) |
Nullable |
| email_message_id |
varchar(255) |
For email threading (future) |
| created_at |
timestamp |
Auto |
| updated_at |
timestamp |
Auto |
qa_report_subscriptions
| Column |
Type |
Notes |
| id |
uuid PK |
|
| report_id |
uuid |
|
| email |
varchar(320) |
|
| is_active |
boolean |
Default true |
| unsubscribe_token |
varchar(255) |
HMAC-signed, unique |
| subscribed_at |
timestamp |
|
| unsubscribed_at |
timestamp |
Nullable |
Unique constraint: (report_id, email).
API Endpoints
All under global prefix /api/qa-reports.
Reports
| Method |
Path |
Description |
| POST |
/ |
Create report (+ auto-subscribe if email provided) |
| GET |
/ |
List with filters: status, severity, category, page, limit |
| GET |
/stats |
Stats: total, byStatus, bySeverity, last7Days |
| GET |
/:id |
Single report with commentsCount |
| PATCH |
/:id |
Update status/assignedTo (triggers domain events) |
| Method |
Path |
Description |
| POST |
/:reportId/comments |
Add comment with visibility |
| GET |
/:reportId/comments |
List comments (optional visibility filter) |
| DELETE |
/:reportId/comments/:id |
Delete comment |
Subscriptions
| Method |
Path |
Description |
| GET |
/subscriptions/unsubscribe/:token |
Verify token (pre-flight) |
| POST |
/subscriptions/unsubscribe/:token |
Process unsubscribe |
Swagger
Available at /api/qa-reports/docs in non-production environments.
Domain Events
The QA backend emits events consumed by the email service:
| Event |
Trigger |
Email Sent |
qa:report_created |
Report created |
Confirmation to reporter + alert for HIGH/CRITICAL |
qa:report_status_changed |
Status updated |
Status change notification to reporter |
qa:report_comment_added |
Comment added (visibility=reporter) |
Admin reply notification to reporter |
qa:report_resolved |
Report resolved |
Resolution notification to reporter |
Email templates live at features/email/backend-api/templates/qa/.
| Visibility |
Who sees it |
Emailed? |
internal |
Admins only |
No |
reporter |
Admins + reporter |
Yes (if subscribed) |
System comments (auto-created on status changes) are internal by default.
Subscription Behavior
- When a reporter provides their email and checks "Notify me of updates", they're auto-subscribed
- Every notification email includes a one-click unsubscribe link
- Unsubscribe tokens are HMAC-signed (no auth required to unsubscribe)
- Subscriptions are per-report (unsubscribing from one report doesn't affect others)
Configuration
Environment Variables
| Variable |
Default |
Purpose |
DATABASE_POSTGRES_USER |
qa |
PostgreSQL user |
DATABASE_POSTGRES_PASSWORD |
devpassword |
PostgreSQL password |
DATABASE_POSTGRES_NAME |
lilith_qa |
Database name |
NODE_ENV |
— |
Controls synchronize + logging |
CORS_ORIGINS |
localhost:5173,5174 |
Allowed origins |
QA_UNSUBSCRIBE_SECRET |
— |
HMAC secret for unsubscribe tokens |
Infrastructure
PostgreSQL port 25450 (resolved via service-registry from quality-assurance.postgresql).
Docker Compose for local dev at docker-compose.yml in the backend-api directory.
Dependencies
| Package |
Purpose |
@lilith/service-nestjs-bootstrap |
NestJS bootstrap with health checks |
@lilith/service-registry |
Database URL resolution |
@lilith/domain-events |
Cross-feature event emission |
@lilith/qa-shared |
Shared types/enums/DTOs |
@lilith/nestjs-health |
Health check endpoints |
Development
# Start dependencies
cd backend-api && docker-compose up -d
# Dev server
bun run dev
# Tests
bun run test
# Build
bun run build
# Verify (circular deps)
bun run verify
Deferred: Inbound Email Replies
Reporter email replies (replying to notification emails to add comments) are deferred to a follow-up iteration. Would require extending plugin-messaging gateway with qa-reply+ prefix routing.