Skip to main content

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

  1. 404

    • Custom 404 page: "Page not found" with link to tickets (useTenantPath) or home. Section 6.8.
  2. 500

    • Custom 500 or error boundary: "Something went wrong" with reload and support link. Section 6.8.
  3. 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.)
  4. 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.
  5. 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

CriterionStatus
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

  1. 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.
  2. 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).
  3. 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

CriterionStatus
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

  1. 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.
  2. 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

CriterionStatus
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

  1. 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.
  2. 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.
  3. Secrets

    • Document required Fly secrets (Section 9); no secrets in repo. Migrations: run with DATABASE_URL_UNPOOLED or equivalent.

Acceptance criteria

CriterionStatus
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

  1. 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).
  2. Logtail

    • Fly.io log forwarding to Logtail (Better Stack); structured logs where possible. Section 3.15.
  3. Uptime

    • Better Stack Uptime (or equivalent) monitoring GET /api/health; alert on downtime. Section 3.15. Health returns { status: "ok", version: "<git-sha>" }.

Acceptance criteria

CriterionStatus
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

  1. RLS verification

    • Document or test: RLS policies on tenant tables prevent cross-tenant access when current_tenant_id set. Section 4.2, 7.3.
  2. Rate limits

    • Confirm auth, API, and worker rate limits in place per Section 4.9; document values.
  3. 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.
  4. 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.
  5. 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

CriterionStatus
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