Milestone 16: Polish, error states, and observability
Purpose: App is production-ready: error and system pages, command palette, profile, accessibility, CI/CD, observability, and security/ops documentation.
Exit state: All error states handled; command palette; profile settings; a11y and responsive pass; GitHub Actions build/test/deploy; Sentry, Logtail, uptime; RLS and key rotation documented.
Spec reference: §3.12 CI/CD, §3.15 Observability, §6.8 Error and system state pages, §6.9 Command palette, §6.10 Profile, Global UI patterns, §7 Security & Compliance.
Prerequisites: M15.
16.1 Error and system state pages
Tasks
-
404
- Custom 404 page: "Page not found" with link to tickets (useTenantPath) or home. Section 6.8.
-
500
- Custom 500 or error boundary: "Something went wrong" with reload and support link. Section 6.8.
-
Tenant not found
- When slug not in DB: show "This workspace doesn't exist" with link to sign in or create org. Section 6.8. (Middleware already 404s; ensure page is friendly.)
-
Locked and maintenance
- Locked: already in M10. Maintenance: /maintenance static page; document that it is served by Fly proxy or dedicated release (app does not toggle). Section 6.8.
-
Inbox disconnected and send failed
- Inbox disconnected banner in ticket view (M07). Send failed: error badge and Retry on message row (M07). Section 6.8. Verify both visible and correct.
Acceptance criteria
| Criterion | Status |
|---|---|
| 404, 500, tenant not found, locked, and maintenance have dedicated pages or handling; copy matches spec. | Pending |
| Inbox disconnected and send-failed states visible in UI. | Pending |
16.2 Command palette and profile
Tasks
-
Command palette
- cmdk (or shadcn command): Cmd+K / Ctrl+K. Section 6.9. Commands: Go to All Tickets, Go to Inbox [name], Go to Settings, Go to Reports; (in ticket view) Change status, Assign, Add tag, Move to inbox; New ticket, Search tickets, Invite team member; Manage inboxes, Manage team, Billing; Toggle theme, Sign out. Filter as user types; keyboard nav (arrows, Enter, Escape). Section 6.9.
-
Profile page
- /app/[slug]/profile. Section 6.10. Tabs: Account (name, email, change password); Notifications (preference matrix — M08); Appearance (theme: light/dark/system). Change password: current password + new + confirm; validate; update user; invalidate other sessions. Section 10.4. Optional: avatar upload (or defer).
-
Delete account (Section 10.4)
- Account tab: "Delete my account" in danger zone. On click: confirm (e.g. type "DELETE" or re-enter password). Before allowing: if user is sole Admin of any tenant, block and show message ("Assign another Admin or delete the workspace first"). On confirm: delete user record and all tenant_memberships; assigned tickets retain assignee_id but display "Deleted user". Redirect to /login. Audit optional (user.deleted or handled by tenant deletion flows).
Acceptance criteria
| Criterion | Status |
|---|---|
| Cmd+K opens palette; all listed commands work; keyboard nav and filter work. | Pending |
| Profile: account edit and password change work; notifications and appearance match M08 and M03. | Pending |
| User can delete own account with confirmation; blocked if sole Admin; on success user and memberships removed. | Pending |
| All roles can access profile (own only). | Pending |
16.3 Accessibility and responsive
Tasks
-
Accessibility
- Keyboard: tab order logical; submit/cancel with keyboard; focus visible. Section 6.10. ARIA and semantics where needed (labels, roles). Color contrast WCAG AA. Audit critical flows (login, ticket list, reply); document any exceptions. Section 2.7.
-
Responsive
- Breakpoints per Section 6.10 (sm 640, md 768, lg 1024, xl 1280). Ticket list and detail: side-by-side on desktop, stacked on small. Tables/cards adapt; no horizontal scroll; touch targets adequate. Section 2.7.
Acceptance criteria
| Criterion | Status |
|---|---|
| Key flows keyboard-navigable; screen reader can understand structure; contrast pass. | Pending |
| Layout adapts; mobile/tablet usable; breakpoints documented. | Pending |
16.4 CI/CD
Tasks
-
GitHub Actions
- Workflow: on push/PR — install (pnpm), lint, build (turbo build), unit/integration tests (Vitest). Section 3.12. Optional: E2E (Playwright) on main or nightly. Section 3.12.
-
Deploy
- Staging: deploy apps/web and apps/worker to Fly.io staging app on push to staging branch; DATABASE_URL to Neon staging branch. Section 4.12. Production: deploy on push to main or tag; same apps; production env and secrets. Build: Dockerfile or Fly build; run migrations in release or separate step.
-
Secrets
- Document required Fly secrets (Section 9); no secrets in repo. Migrations: run with DATABASE_URL_UNPOOLED or equivalent.
Acceptance criteria
| Criterion | Status |
|---|---|
| CI runs on PR; lint and build pass; tests run. | Pending |
| E2E (Playwright) runs in CI — on PR or main/nightly per INDEX Testing strategy. | Pending |
| Staging deploy from staging branch works; production deploy from main/tag works. | Pending |
| Migrations run before or during deploy; no manual step if possible. | Pending |
16.5 Observability
Tasks
-
Sentry
- apps/web and apps/worker: Sentry init; capture unhandled errors; attach tenant_id and inbox_id as tags (never PII). Section 3.15. beforeSend: strip credentials, tokens, email body. Source maps uploaded at build (CI); not in production bundle. Environment: production, staging, development. Sample rate: 100% errors; 10% performance (configurable).
-
Logtail
- Fly.io log forwarding to Logtail (Better Stack); structured logs where possible. Section 3.15.
-
Uptime
- Better Stack Uptime (or equivalent) monitoring GET /api/health; alert on downtime. Section 3.15. Health returns
{ status: "ok", version: "<git-sha>" }.
- Better Stack Uptime (or equivalent) monitoring GET /api/health; alert on downtime. Section 3.15. Health returns
Acceptance criteria
| Criterion | Status |
|---|---|
| Errors in web and worker reported to Sentry; tags and beforeSend verified; no secrets in events. | Pending |
| Logs in Logtail; health endpoint monitored; alerts configured. | Pending |
| Ready for M17 (Add-ons) or launch. | Pending |
16.6 Security and ops documentation
Tasks
-
RLS verification
- Document or test: RLS policies on tenant tables prevent cross-tenant access when current_tenant_id set. Section 4.2, 7.3.
-
Rate limits
- Confirm auth, API, and worker rate limits in place per Section 4.9; document values.
-
Key rotation and backup
- Runbook: ENCRYPTION_KEY rotation (decrypt/re-encrypt credentials and tokens); AUTH_SECRET rotation (invalidate sessions). Section 7.1. Backup and recovery: Neon PITR and restore steps; S3 and attachment restore (Section 7.7). Document in ops or docs.
-
Audit log retention pruning (Section 2.9)
- Weekly Inngest cron: for each tenant, delete audit_logs rows older than tenant's retention period (Starter 30d, Growth 90d, Business 1y, Enterprise from plan or custom). Retention by plan documented in runbook. M09 defines retention values; M16 implements or documents the cron.
-
Data export (v1 / GDPR, Section 7.4)
- Settings → General (or Profile): "Request data export" or "Data export" — document manual process in runbook (e.g. support generates export on request) or implement "Request export" that creates a ticket/email to support. Full account export (ZIP of JSON) is post-launch; v1 uses manual process for GDPR.
Acceptance criteria
| Criterion | Status |
|---|---|
| RLS verified or documented; rate limits documented. | Pending |
| Key rotation and backup/restore runbooks exist; team can execute. | Pending |
| Audit log retention: weekly pruning cron or runbook doc per plan (Starter 30d, Growth 90d, Business 1y). | Pending |
| Data export: manual process documented or "Request export" flow in place for v1 GDPR. | Pending |
| Milestone 16 sign-off: production-ready; M17 optional. | Pending |
| Ready for M17 (Add-ons) or launch. | Pending |