learning_ai_common_plat/docs/devops/USER_ISSUE_REPORTING_ROADMAP.md
2026-03-03 07:20:56 -08:00

14 KiB

User-Initiated Issue Reporting with Screenshots — Implementation Roadmap

Location: docs/devops/USER_ISSUE_REPORTING_ROADMAP.md
Status: Draft
Created: 2026-03-02


Overview

Enable users to self-report issues with automatic/manual screenshots and comments. This extends the existing feedback module to support rich media attachments, leveraging the blob module for Azure Blob storage.

Use Cases

  1. Bug Report with Screenshot — User encounters error → taps "Report Issue" → screenshot captured → typed description submitted
  2. Feature Request with Mockup — User uploads annotated screenshot showing desired UI change
  3. Crash Auto-Report — App crashes → on restart, user prompted to "Send crash report with screenshot"
  4. Support Chat Attachment — Screenshots attached to existing feedback for back-and-forth with support team

Current State

Existing Feedback Module

Component Status
POST /api/feedback Creates feedback (type, title, body, screen)
GET /api/feedback Admin list/query
PUT /api/feedback/:id Admin triage (status, adminNotes)
Screenshot support Only text screen field
Multiple attachments Not supported
Threaded comments Not supported

Existing Blob Module (Reusable)

Component Status
POST /api/blob/sas Generate SAS URL for direct upload
Azure Blob integration Storage + lifecycle
Container management Per-product isolation

Target Architecture

User Flow:
1. User taps "Report Issue" in app
2. Client captures screenshot (optional annotation)
3. Client requests SAS URL: POST /api/feedback/sas
4. Client uploads image directly to Azure Blob (to feedbackScreenshots container)
5. Client submits feedback: POST /api/feedback (with screenshotBlobPath, contentType, sizeBytes)
6. Admin views feedback in dashboard with image preview (fresh SAS URL generated on-demand)

Data Model Changes

Option A: Inline Screenshot (Simple)

// feedback/types.ts — Add to existing FeedbackDoc
interface FeedbackDoc {
  // ... existing fields ...
  
  // Screenshot attachment (single) — SAS URLs generated on-demand, not stored
  screenshotBlobPath?: string;        // "feedbackScreenshots/{productId}/{feedbackId}/{screenshotId}.png"
  screenshotContentType?: 'image/png' | 'image/jpeg' | 'image/webp';
  screenshotSizeBytes?: number;         // Actual uploaded size
  
  // Device context for debugging
  deviceContext?: {
    osVersion: string;
    appVersion: string;
    deviceModel: string;
    screenResolution: string;
    locale: string;
  };
}

// Add to CreateFeedbackSchema
screenshotBlobPath: z.string().optional(),
screenshotContentType: z.enum(['image/png', 'image/jpeg', 'image/webp']).optional(),
screenshotSizeBytes: z.number().int().min(1).max(5 * 1024 * 1024).optional(), // 5MB limit
deviceContext: z.object({
  osVersion: z.string(),
  appVersion: z.string(),
  deviceModel: z.string(),
  screenResolution: z.string(),
  locale: z.string(),
}).optional(),

Option B: Separate FeedbackAttachments Container (Extensible)

// feedback/types.ts — New attachment model
interface FeedbackAttachmentDoc {
  id: string;                         // att_<uuid>
  feedbackId: string;                 // Parent feedback (partition key)
  productId: string;
  
  // Blob storage
  blobPath: string;                   // "feedback/{productId}/{feedbackId}/{id}.png"
  blobUrl: string;                    // SAS URL (refreshed on fetch)
  containerName: string;              // e.g., "user-feedback"
  
  // Metadata
  fileName: string;
  contentType: 'image/png' | 'image/jpeg' | 'image/webp';
  sizeBytes: number;
  width: number;
  height: number;
  
  // Capture context
  capturedAt: string;
  trigger: 'manual' | 'auto_crash' | 'auto_error';
  screenName?: string;
  
  createdAt: string;
}

interface FeedbackDoc {
  // ... existing fields ...
  
  // Reference to attachments
  attachmentCount: number;            // Denormalized counter
  hasScreenshot: boolean;            // Quick check for UI
}

// API changes
POST /api/feedback/:id/attachments      // Add attachment to existing feedback
GET  /api/feedback/:id/attachments      // List attachments
DELETE /api/feedback/:id/attachments/:attId  // Remove attachment (admin)

Recommendation: Start with Option A (single screenshot), migrate to Option B if multi-attachment demand arises.


API Specification

New Endpoints

Method Endpoint Auth Rate Limit Description
POST /api/feedback/sas User 5 per 10 min Get SAS URL for screenshot upload
POST /api/feedback User 10 per hour Submit feedback (with optional screenshotBlobPath)
GET /api/feedback/:id/screenshot Admin 30 per hour Get fresh SAS URL for viewing screenshot
DELETE /api/feedback/:id/screenshot Admin 10 per hour Delete screenshot (GDPR/privacy)

SAS Generation Endpoint

// POST /api/feedback/sas
// Request
{
  "contentType": "image/png",
  "sizeHint": 1024000  // Optional: 1MB hint for validation
}

// Response (201)
{
  "blobPath": "feedbackScreenshots/lysnrai/fb_abc123/screenshot_xyz.png",
  "uploadUrl": "https://bytelyst.blob.core.windows.net/...?sv=...",
  "expiresIn": 300,  // 5 minutes
  "maxSizeBytes": 5242880  // 5MB limit
}

Submit Feedback with Screenshot

// POST /api/feedback
// Request
{
  "type": "bug",
  "title": "App crashes when tapping record",
  "body": "Steps: 1. Open app 2. Tap red button 3. Crash",
  "screen": "RecordingScreen",
  "screenshotBlobPath": "feedbackScreenshots/lysnrai/fb_abc123/screenshot_xyz.png",
  "deviceContext": {
    "osVersion": "iOS 17.4",
    "appVersion": "2.3.1",
    "deviceModel": "iPhone15,2",
    "screenResolution": "393x852",
    "locale": "en-US"
  }
}

Implementation Phases

Phase 1: Server Foundation (2-3 days) — COMPLETE

1.1 Data Model Extension — acfbd7c

  • Add screenshotBlobPath to FeedbackDoc interface
  • Add deviceContext to FeedbackDoc interface
  • Update CreateFeedbackSchema with new fields
  • Add feedback_screenshots container to cosmos-init.ts (if Option B) — Skipped (using blob storage)

1.2 Repository Layer — 8d2ba9c

  • Extend createFeedback() to handle screenshot metadata
  • Add generateScreenshotSas() function (wrapper around blob module)
  • Add getFeedbackWithScreenshot() with fresh SAS URL generation
  • Add hasScreenshot() helper for quick UI checks

1.3 API Routes — cfbaa92

  • POST /api/feedback/sas — Generate upload URL
    • Rate limit: 5 requests per 10 minutes per user
    • Validate content type (image/* only)
    • Return blob path + SAS URL
  • Update POST /api/feedback — Accept screenshot metadata
  • GET /api/feedback/:id/screenshot — Get fresh view URL
  • DELETE /api/feedback/:id/screenshot — Admin delete

1.4 Integration — Already wired in server.ts

  • Wire new routes into server.ts — Feedback routes already registered
  • Add blob container feedbackScreenshots to blob module config — d876bb0
  • Set lifecycle policy: 90-day TTL for user screenshots — TODO: Azure lifecycle policy

1.5 Testing — e712968

  • Unit tests for SAS generation (schema validation)
  • Unit tests for feedback with screenshot submission
  • Integration test: full flow (SAS → upload → submit → view) — TODO: requires blob storage
  • GDPR deletion test — TODO: Phase 3

Phase 2: Client SDK Updates (3-4 days) — PARTIAL

2.1 TypeScript SDK (@bytelyst/feedback-client) — b261cda

  • FeedbackClient.submitWithScreenshot(params)
    • Internal flow: get SAS → upload blob → submit feedback
    • Progress callbacks for upload
  • FeedbackClient.captureAndSubmit() — Placeholder (TODO-1: platform-specific capture)
  • Vitest tests for client

2.2 Swift SDK (iOS) — TODO

  • FeedbackManager.submit(title:body:screenshot:)
  • FeedbackManager.captureScreenshot() — UIImage → blob upload
  • Annotation overlay (optional drawing on screenshot)

2.3 Kotlin SDK (Android) — TODO

  • FeedbackManager.submitWithScreenshot()
  • MediaProjection integration for screenshot capture
  • Composable for in-app feedback sheet with screenshot preview

Phase 3: Admin Dashboard UI (2-3 days) — COMPLETE

3.1 Feedback List Enhancements — d4e7f8a

  • Screenshot indicator (badge) in list view
  • Filter: "Has screenshot" / "No screenshot"

3.2 Feedback Detail View — d4e7f8a

  • Full-size screenshot display (lightbox)
  • Device context panel (OS version, app version, screen resolution)
  • "Delete screenshot" (GDPR compliance)
  • "Download screenshot" button — TODO: add direct download

3.3 Client Library — d4e7f8a

  • Admin dashboard feedback page with screenshot viewer

Privacy & Security Considerations

PII Handling

  • Screenshots may contain sensitive user data
  • Blur regions API (client-side before upload)
  • Auto-redact: Detect and blur common PII areas (email fields, phone numbers)
  • User consent: "This screenshot may include your data. Continue?"

Retention

  • Screenshot TTL: 90 days (match feedback lifecycle)
  • On feedback deletion → cascade delete screenshot blob
  • User-initiated deletion: "Delete my feedback and screenshot"

Access Control

  • Users submit screenshots but cannot directly view/download them (security)
  • Admins can view all screenshots for their product via fresh SAS URLs
  • SAS URLs are time-limited (15 minutes) and generated on-demand per view

Open Questions

# Question Impact Suggested Answer
1 Max screenshot size? Storage cost 5MB limit, WebP compression recommended
2 Multiple screenshots per feedback? Complexity Start with 1, add array support later
3 Screenshot annotation/drawing? Client complexity Phase 2.2 for iOS, others skip
4 Auto-capture on crash? Privacy risk Opt-in only, show preview before submit
5 Video screen recording? Storage cost Future Phase 4, not now
6 Anonymous feedback allowed? Auth complexity No — require auth for accountability

Success Metrics

  • Adoption: 30% of bug reports include screenshot within 3 months
  • Resolution time: -20% time-to-resolution for reports with screenshots
  • Storage: <100GB/month for screenshots (at 1000 reports/day, 100KB avg)

Appendix: File Changes

New Files

services/platform-service/src/modules/feedback/
  └── attachment.types.ts       # (if Option B)

Modified Files

services/platform-service/src/modules/feedback/
  ├── types.ts                  # Add screenshot fields
  ├── repository.ts             # Add SAS functions
  └── routes.ts                 # Add SAS endpoint

services/platform-service/src/lib/cosmos-init.ts
  └── Add feedback_attachments container (if Option B)

services/platform-service/src/server.ts
  └── Register feedback SAS routes

TODOs Completed

# TODO Status Commit
TODO-1 Platform-specific screenshot capture (Web) Complete 921f211
TODO-1 (RN) React Native screenshot capture placeholder Complete 32c477e
TODO-1 (Electron) Electron screenshot capture placeholder Complete 32c477e
TODO-2 Swift SDK (iOS) - BLFeedbackClient.swift Complete Latest
TODO-3 Kotlin SDK (Android) - BLFeedbackClient.kt Complete Latest
TODO-4 Integration tests for full SAS flow Complete bd19d44
TODO-5 GDPR deletion test Complete Latest
TODO-7 Azure lifecycle policy configuration Complete Latest

Remaining TODOs

# TODO Priority Notes
TODO-6 Download screenshot button Fixed Already implemented

Last Updated: 2026-03-03

Bugs Fixed During Review (2026-03-02)

# Bug Fix
1 Inconsistent fields: screenshotUrl/screenshotUrlExpiresAt not used Removed - SAS URLs generated on-demand
2 Blob path pattern mismatch Updated to feedbackScreenshots/{productId}/{feedbackId}/{screenshotId}.png
3 Flow showed "temp path then move" but implementation uploads directly Clarified flow to show direct upload to final container
4 Missing rate limiting specs Added rate limit column to endpoint table
5 Privacy claimed "users can view own screenshots" but only admin endpoint exists Clarified users submit but cannot view (security)
6 Appendix listed sas.ts file that wasn't created Removed - SAS logic is in routes.ts
7 Inconsistent size limits: 5MB vs 10MB Aligned to 5MB throughout
8 Missing screenshotContentType and screenshotSizeBytes Added to data model example

Bugs Fixed During Second Review (2026-03-03)

# Bug Fix Commit
9 Search icon positioning broken Added relative to parent div, pointer-events-none to icon Latest
10 Toast variant 'destructive' not valid Changed to 'error' to match type definition Latest
11 Routes referencing removed fields Removed screenshotUrl/screenshotUrlExpiresAt from delete endpoint Latest
12 Misleading blob path comment Fixed comment to reflect blob stays at initial location Latest
13 Missing download screenshot button Added downloadScreenshot() function and UI button Latest