Files
nick-tracker/PROMPT.md
2026-01-11 07:51:30 +00:00

30 KiB
Raw Permalink Blame History

PROMPT.md nick-tracker (AutoScheduler GTD System)

Objective

Build AutoScheduler, a self-hosted web application implementing the Getting Things Done (GTD) methodology with automatic calendar scheduling. The system ingests tasks from multiple sources (manual capture, email via IMAP/Microsoft Graph, read-only ConnectWise Manage sync), processes them through GTD workflows (Inbox → Next Actions/Projects/Waiting For/Someday-Maybe/Tickler), and automatically schedules actionable tasks into available calendar slots from CalDAV-compatible sources (Nextcloud, Google Calendar, Outlook). The scheduling engine respects user-defined working hours, context constraints (@desk, @phone, @errand, @homelab, @anywhere), deadlines, and manual priorities, batching similar tasks when possible and automatically rescheduling displaced tasks when calendar conflicts arise. A React SPA with interactive week-view calendar supports drag-and-drop manual overrides and task locking. Weekly Review interface auto-schedules and presents comprehensive system review. Real-time notifications via WebSocket, email, and optional webhook inform users of schedule changes and follow-up reminders.

Core Value Proposition: Unified GTD task management across work (ConnectWise) and personal domains (Homelab, Daily Routines, House, Professional Development) with intelligent automatic scheduling that respects context, time constraints, and priorities, eliminating manual time-blocking while maintaining GTD methodology integrity.


Application Type

web Self-hosted web application with React SPA frontend and NestJS backend API, accessed via browser. Containerized deployment via Docker Compose for user-managed infrastructure. No native desktop or mobile apps required; responsive web UI serves all devices.


Architecture

Core Components

  1. Frontend (React SPA)

    • Interactive week-view calendar with drag-and-drop task scheduling
    • GTD workflow interfaces: Inbox processing, Next Actions, Projects, Weekly Review
    • Real-time updates via WebSocket for automatic rescheduling notifications
    • Task capture quick-add form and external REST API endpoint
    • Settings UI for calendar connections, email/ConnectWise integration, working hours
  2. Backend (NestJS API)

    • REST API for CRUD operations on tasks, projects, inbox items, connections
    • WebSocket gateway for real-time notifications and schedule updates
    • Scheduled jobs (cron) for: email polling, ConnectWise sync, calendar sync, Tickler activation, Weekly Review scheduling
    • Task scheduling engine with constraint satisfaction and conflict resolution
    • Integration services: CalDAV client, Microsoft Graph client, Google Calendar API, ConnectWise Manage API, IMAP/SMTP
  3. Database (PostgreSQL)

    • Relational schema: Users, InboxItems, Tasks, Projects, CalendarEvents, ReschedulingEvents, Notifications
    • Foreign keys enforce referential integrity; indexes optimize scheduler queries
    • JSON columns for flexible metadata (workingHours, notificationPreferences, sourceMetadata)
  4. Message Queue (Redis + Bull)

    • Asynchronous job processing for long-running syncs (ConnectWise, email, calendar)
    • Rate limiting and retry logic for external API calls
    • Queue-based scheduling engine to prevent concurrent conflicts
  5. Container Orchestration (Docker Compose)

    • Services: backend (NestJS), database (PostgreSQL), cache (Redis), frontend (nginx serving static build)
    • Volumes for PostgreSQL persistence, file uploads (reference materials)
    • Health checks, restart policies, environment-based configuration

Interfaces

  • REST API: Full CRUD for all entities, connection management, manual scheduling overrides, Weekly Review operations
  • WebSocket: Real-time push notifications for rescheduling events, Waiting For follow-ups, Tickler activations
  • External Integrations: CalDAV (Nextcloud/generic), Microsoft Graph (Outlook/Google Calendar via OAuth), ConnectWise Manage REST API, IMAP/SMTP

Deployment

self_hosted Docker Compose stack deployed to user's infrastructure (home server, VPS). Users manage environment variables (API keys, DB credentials), backups, and reverse proxy (Traefik/nginx) for HTTPS. Optional OIDC authentication for enterprise users; local account authentication default.


Tech Stack

  • Language: TypeScript (strict mode, ES2022 target)
  • Runtime: Node.js 20 LTS
  • Backend Framework: NestJS 10 (modules, dependency injection, guards, interceptors)
  • Frontend Framework: React 18 (functional components, hooks) + Vite 5 (build tool)
  • Database: PostgreSQL 16 (relational, JSONB support)
  • ORM: TypeORM 0.3 (Active Record pattern, migrations, query builder)
  • State Management: Zustand (lightweight, minimal boilerplate)
  • Data Fetching: TanStack Query (React Query v5) for server state caching
  • Routing: React Router v6 (nested routes, loaders)
  • Calendar UI: FullCalendar (drag-drop, resource timeline, week view)
  • Drag-and-Drop: react-beautiful-dnd (accessible drag-drop for task lists)
  • WebSocket: socket.io (NestJS WS adapter, auto-reconnect)
  • Job Queue: Bull (Redis-backed task queues, cron scheduling)
  • Cache: Redis 7 (Bull queue storage, session cache)
  • Authentication: Passport.js (JWT strategy, optional OIDC)
  • Validation: class-validator, class-transformer (backend DTOs), Zod (frontend forms)
  • Styling: TailwindCSS 3 + Radix UI (accessible component primitives)
  • Date Handling: date-fns (timezone-aware, immutable)
  • Calendar Protocols: ical.js (iCal parsing), node-caldav (CalDAV client)
  • External APIs: @microsoft/microsoft-graph-client (Outlook/Graph), axios (ConnectWise REST), imap (email polling)
  • Testing: Jest (backend unit/integration), Vitest (frontend unit), Supertest (API e2e)
  • Containerization: Docker, Docker Compose v2
  • Monorepo: pnpm workspaces (shared types package)
  • Build Tools: tsup (backend bundling), Vite (frontend HMR and build)

Phases & Completion Criteria

Phase 1: Foundation

Goal: Establish project structure, core infrastructure, authentication, and basic database schema. Prove Docker Compose stack runs and REST API accepts requests.

Completion Criteria

  • Monorepo initialized with pnpm workspaces: packages/backend, packages/frontend, packages/shared-types. Root package.json has workspace scripts: pnpm dev, pnpm build, pnpm test. Verify: pnpm install && pnpm -r list shows all three workspaces.
  • Backend NestJS app scaffolded with modules: AuthModule, UsersModule, TasksModule, ProjectsModule, InboxModule. Verify: pnpm --filter backend build succeeds, dist/ contains compiled JS.
  • Frontend React app created with Vite, TailwindCSS configured, Radix UI installed. Basic route structure: /login, /inbox, /calendar, /projects, /settings. Verify: pnpm --filter frontend dev starts dev server on port 5173, homepage renders.
  • PostgreSQL database schema created via TypeORM migrations: User, InboxItem, Task, Project, CalendarConnection, CalendarEvent, ConnectWiseConnection, EmailConnection, ReferenceMaterial, ReschedulingEvent, Notification entities with all fields and relationships from spec. Verify: pnpm --filter backend migration:run succeeds, psql -d autoscheduler -c "\dt" lists 11 tables.
  • Docker Compose stack configured: backend, postgres, redis, frontend services. Backend exposes port 3000, frontend port 80, PostgreSQL port 5432 (internal only). Environment variables loaded from .env file. Verify: docker compose up -d && docker compose ps shows all services healthy, curl http://localhost:3000/health returns {"status":"ok"}.
  • Authentication implemented: JWT-based auth with Passport.js. /api/v1/auth/register, /api/v1/auth/login, /api/v1/auth/refresh endpoints functional. Password hashing with bcrypt (12 rounds). JWT guard protects all routes except auth endpoints. Verify: curl -X POST http://localhost:3000/api/v1/auth/register -d '{"email":"test@example.com","password":"SecurePass123!","name":"Test User","timezone":"America/New_York"}' -H "Content-Type: application/json" returns 201 with JWT, subsequent curl -H "Authorization: Bearer <token>" http://localhost:3000/api/v1/users/me returns user object.
  • Frontend authentication flow: Login form posts to /api/v1/auth/login, stores JWT in memory (Zustand), redirects to /inbox. Protected routes require auth token, redirect to /login if missing. Axios interceptor adds Authorization header. Verify: Manual test in browser: register user, log in, see redirect to inbox, refresh page maintains session.
  • Basic error handling: Global exception filter in NestJS logs errors, returns standardized JSON error responses (status, message, timestamp). Frontend axios interceptor catches 401, clears token, redirects to login. Verify: curl http://localhost:3000/api/v1/nonexistent returns 404 JSON, curl -H "Authorization: Bearer invalid" http://localhost:3000/api/v1/users/me returns 401 JSON.
  • Logging configured: NestJS Logger configured with timestamps, log levels (dev: debug, prod: info). Winston logger optional enhancement but basic Logger functional. Verify: Backend console shows structured logs on startup and per request.
  • Type safety enforced: shared-types package exports DTOs (CreateTaskDto, TaskResponseDto, etc.) used by both backend validators and frontend Zod schemas. Verify: Change a field type in shared-types, run pnpm build, see TypeScript errors in both frontend and backend if mismatched.
  • Health checks: /health endpoint returns database connection status, Redis connection status. Docker Compose health checks configured for backend (GET /health every 30s). Verify: curl http://localhost:3000/health returns {"status":"ok","database":"connected","redis":"connected"}, docker compose ps shows backend as healthy.

Phase 1 Verification Command:

pnpm install && pnpm build && docker compose up -d && sleep 10 && \
curl -f http://localhost:3000/health && \
curl -X POST http://localhost:3000/api/v1/auth/register \
  -d '{"email":"verify@test.com","password":"Test123!","name":"Verify User","timezone":"UTC"}' \
  -H "Content-Type: application/json" | grep -q "token" && \
echo "✓ Phase 1 Complete"

Phase 2: Core Features

Goal: Implement GTD workflows (Inbox capture and processing, Next Actions, Projects, Someday/Maybe, Waiting For, Tickler), basic scheduling engine, and interactive calendar week view with drag-and-drop.

Completion Criteria

  • Inbox capture endpoints: POST /api/v1/inbox creates unprocessed InboxItem with source=MANUAL. Fields: content (text), source (enum), sourceMetadata (JSON). Returns created item with ID. Verify: curl -X POST -H "Authorization: Bearer <token>" http://localhost:3000/api/v1/inbox -d '{"content":"Test inbox item"}' -H "Content-Type: application/json" returns 201 with {"id":"uuid","content":"Test inbox item","processed":false}.
  • Inbox list and processing: GET /api/v1/inbox returns all unprocessed items for authenticated user. POST /api/v1/inbox/:id/process accepts action payload (e.g., {"action":"task","context":"@desk","domain":"work","title":"Do thing"}) and converts inbox item to Task, Project, or marks as Trash, setting processed=true. Verify: Create inbox item, process to task, GET inbox returns empty array, GET /api/v1/tasks includes new task.
  • Task CRUD: Full CRUD endpoints for Tasks with validation: POST /api/v1/tasks (required: title, domain; optional: context, priority, dueDate, estimatedDuration, projectId), GET /api/v1/tasks (with filters: status, context, domain), PATCH /api/v1/tasks/:id (partial updates), DELETE /api/v1/tasks/:id (soft delete or hard delete based on policy). Task status enum: NEXT_ACTION, WAITING_FOR, SOMEDAY_MAYBE, TICKLER, COMPLETED. Context enum: DESK, PHONE, ERRAND, HOMELAB, ANYWHERE. Domain enum: WORK, HOMELAB, DAILY_ROUTINES, HOUSE, PROFESSIONAL_DEVELOPMENT. Verify: Create task with all fields, GET task by ID returns full object, PATCH updates priority, DELETE removes task.
  • Task filtering views: Separate endpoints or query params for GTD views: GET /api/v1/tasks?status=NEXT_ACTION (Next Actions), GET /api/v1/tasks?status=WAITING_FOR (Waiting For items with follow-up dates), GET /api/v1/tasks?status=SOMEDAY_MAYBE (Someday/Maybe list), GET /api/v1/tasks?status=TICKLER (future Tickler items). Verify: Create 5 tasks with different statuses, each GET returns only matching tasks.
  • Project CRUD: Full CRUD for Projects: POST /api/v1/projects (required: name, domain; optional: description, desiredOutcome, connectwiseProjectId), GET /api/v1/projects (filter by status: active, on-hold, completed, domain), PATCH /api/v1/projects/:id, DELETE /api/v1/projects/:id. Verify: Create project, assign task to project via PATCH /api/v1/tasks/:id with projectId, GET /api/v1/projects/:id/tasks returns associated tasks.
  • Reference material attachments: POST /api/v1/projects/:id/reference accepts multipart file upload or URL/text reference. Creates ReferenceMaterial entity linked to project. File uploads stored in Docker volume, path saved in DB. GET /api/v1/projects/:id includes references array. Verify: Upload PDF to project, GET project returns reference with file path, file accessible at /uploads/:filename route.
  • Waiting For follow-up dates: Tasks with status=WAITING_FOR accept followUpDate timestamp. Scheduled job (cron) runs daily, identifies Waiting For items where followUpDate <= NOW(), creates Notification for user. Verify: Create Waiting For task with followUpDate tomorrow, manually trigger cron (POST /api/v1/debug/trigger-cron), next day notification appears in GET /api/v1/notifications.
  • Tickler activation: Tasks with status=TICKLER have ticklerDate. Daily cron job checks for ticklerDate <= NOW(), converts to InboxItem with source=TICKLER, status=PROCESSED, creates notification. Verify: Create Tickler task for tomorrow, run cron job manually, InboxItem appears with content from task title, notification created.
  • User preferences: GET /api/v1/user/preferences returns user's workingHours (JSON: {"monday":{"start":"09:00","end":"17:00"},...}), timezone, notificationPreferences (JSON: {"email":true,"webhook":false,"webhookUrl":""}). PATCH /api/v1/user/preferences updates preferences. Verify: Update working hours, GET preferences returns new hours.
  • Basic scheduling engine: Service (SchedulingService) finds available time slots in user's calendar based on working hours and existing CalendarEvents. POST /api/v1/schedule/regenerate triggers scheduling: fetches all NEXT_ACTION tasks without scheduledStart, assigns scheduledStart/scheduledEnd within available slots, respecting estimatedDuration (default 30 min) and constraints (work contexts only during work hours). Persists scheduled times to Task table. Returns scheduled task count. Verify: Create 3 Next Action tasks (2 @desk, 1 @phone), set working hours 9-5, run regenerate, GET tasks shows scheduledStart/scheduledEnd within 9-5.
  • Context-based batching: Scheduling engine groups consecutive tasks with same context when possible (e.g., 3 @phone tasks scheduled 10:00, 10:30, 11:00). Verify: Create 5 @phone tasks, regenerate schedule, GET schedule shows consecutive phone blocks.
  • Priority and deadline respect: Scheduling engine sorts tasks by manual priority (1=highest) and dueDate before assigning slots. Higher priority tasks scheduled earlier in available slots. Verify: Create 3 tasks with priorities 1, 2, 3 and no due dates, regenerate, task with priority=1 gets earliest slot; create task with dueDate=today, regenerate, it schedules before lower-priority tasks with later due dates.
  • Frontend inbox UI: React component renders unprocessed inbox items as list. Quick-add form at top posts to /api/v1/inbox. Each item has "Process" button opening modal with GTD decision tree (Is it actionable? → Yes: Is it multi-step? → Project vs. Next Action; No: Reference/Trash). Modal form submits to /api/v1/inbox/:id/process. Optimistic UI updates with React Query mutations. Verify: Manual browser test: add inbox item, process to Next Action with @desk context, see item disappear from inbox.
  • Frontend calendar week view: FullCalendar component configured with timeGridWeek view, displays Tasks with scheduledStart/scheduledEnd as events. Color-coded by context (custom CSS or FullCalendar event color prop). Verify: Create 5 scheduled tasks, navigate to /calendar, see tasks rendered in week grid with correct times and colors.
  • Drag-and-drop manual scheduling: FullCalendar eventDrop callback fires on drag, PATCH /api/v1/tasks/:id with new scheduledStart/scheduledEnd, sets isLocked=true to prevent auto-rescheduling. eventResize callback handles duration changes. Verify: Drag task in calendar, reload page, task remains at new time, isLocked=true in DB.
  • Task lock/unlock: POST /api/v1/tasks/:id/lock sets isLocked=true, POST /api/v1/tasks/:id/unlock sets isLocked=false. Scheduling engine skips locked tasks. Lock icon displayed in calendar for locked tasks. Verify: Lock task, run schedule regenerate, task time unchanged; unlock, regenerate, task can move.
  • Next Actions list UI: React component at /next-actions fetches GET /api/v1/tasks?status=NEXT_ACTION, renders grouped by context. Filter dropdown by context. Click task opens detail modal with edit form. Verify: Create 10 Next Actions across 3 contexts, filter by @phone, see only phone tasks.
  • Projects list UI: React component at /projects fetches GET /api/v1/projects, renders cards with name, domain, active task count. Click card navigates to /projects/:id detail view showing tasks, references, edit form. Verify: Create 3 projects with tasks, navigate to project detail, see tasks listed, add reference material, see it appear.
  • Waiting For UI: React component at /waiting-for fetches GET /api/v1/tasks?status=WAITING_FOR, displays with follow-up dates. Overdue follow-ups highlighted red. Verify: Create Waiting For task with past follow-up date, see red highlight.
  • Someday/Maybe UI: React component at /someday-maybe fetches GET /api/v1/tasks?status=SOMEDAY_MAYBE, allows activation (status change to NEXT_ACTION). Verify: Create Someday task, click "Activate" button, task disappears from Someday list, appears in Next Actions.

Phase 2 Verification Command:

TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -d '{"email":"verify@test.com","password":"Test123!"}' \
  -H "Content-Type: application/json" | jq -r .token) && \
curl -f -X POST -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/inbox \
  -d '{"content":"Phase 2 verify"}' -H "Content-Type: application/json" && \
TASK_ID=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/tasks \
  -d '{"title":"Test task","domain":"WORK","context":"DESK"}' \
  -H "Content-Type: application/json" | jq -r .id) && \
curl -f -X POST -H "Authorization: Bearer $TOKEN" \
  http://localhost:3000/api/v1/schedule/regenerate && \
curl -s -H "Authorization: Bearer $TOKEN" \
  http://localhost:3000/api/v1/tasks/$TASK_ID | jq .scheduledStart | grep -q "T" && \
echo "✓ Phase 2 Complete"

Phase 3: Integration

Goal: Implement external integrations (CalDAV, Microsoft Graph, ConnectWise Manage, IMAP email capture), real-time WebSocket notifications for rescheduling, conflict detection and automatic rescheduling, Weekly Review scheduling and interface.

Completion Criteria

  • CalDAV connection CRUD: POST /api/v1/connections/calendar creates CalendarConnection with provider=CALDAV, calendarUrl, credentials (username/password encrypted at rest). GET /api/v1/connections/calendar lists user's connections. DELETE /api/v1/connections/calendar/:id removes connection. Verify: Create CalDAV connection with Nextcloud URL, GET returns connection with masked credentials.
  • CalDAV sync job: CalendarSyncService using node-caldav library queries CalDAV server for events in date range (next 30 days). Creates/updates CalendarEvent entities with externalId, startTime, endTime. Cron job runs every 15 minutes. Manual trigger: POST /api/v1/connections/calendar/:id/sync. Verify: Configure CalDAV connection to test Nextcloud instance, create event in Nextcloud, trigger sync, GET /api/v1/calendar/events?start=<today>&end=<+7days> returns event.
  • Microsoft Graph calendar connection: POST /api/v1/connections/calendar with provider=MICROSOFT_GRAPH initiates OAuth flow, stores refresh token. CalendarSyncService uses @microsoft/microsoft-graph-client to fetch events from Outlook. Verify: Mock Microsoft Graph client in tests to return sample events, trigger sync, events persisted.
  • Google Calendar API connection: POST /api/v1/connections/calendar with provider=GOOGLE initiates OAuth flow (or accepts service account JSON). CalendarSyncService uses Google Calendar API via axios/googleapis. Verify: Mock Google API responses, trigger sync, events persisted with externalId.
  • ConnectWise Manage connection: POST /api/v1/connections/connectwise accepts companyId, publicKey, privateKey, apiUrl, memberId. ConnectWiseService using axios queries /service/tickets, /project/tickets, /project/projects with conditions owner/id={memberId}. Creates InboxItems with source=CONNECTWISE, sourceMetadata includes ticketId, priority, SLA. Cron job runs hourly. Verify: Mock ConnectWise API responses (3 tickets), trigger sync, inbox has 3 items with sourceMetadata.
  • ConnectWise zero-ticket projects: ConnectWise sync queries projects assigned to user with /project/projects/:id/tickets count=0. Creates InboxItem with content="Plan project: {projectName}" and sourceMetadata.connectwiseProjectId. Verify: Mock API returns project with zero tickets, sync creates planning task inbox item.
  • ConnectWise priority display: Tasks sourced from ConnectWise store connectwisePriority and connectwiseSLA fields (strings). Displayed in UI for reference but do not affect scheduling priority (user manually sets priority integer). Verify: Process ConnectWise inbox item to task, task has connectwisePriority="High", priority=null, user updates priority=1, scheduling uses user priority.
  • IMAP email connection: POST /api/v1/connections/email with provider=IMAP accepts imapHost, imapPort, credentials, inboxFolder. EmailSyncService using imap library connects, fetches UNSEEN messages, creates InboxItem with content=email subject+body, sourceMetadata includes from, date. Marks email as SEEN. Cron job runs every 5 minutes. Verify: Mock IMAP responses, trigger sync, inbox items created from emails.
  • Microsoft Graph email connection: POST /api/v1/connections/email with provider=MICROSOFT_GRAPH uses OAuth token to fetch messages from inbox folder via Graph API. Same InboxItem creation logic. Verify: Mock Graph API responses, sync creates inbox items.
  • Conflict detection on calendar sync: After calendar sync, ConflictDetectionService compares new CalendarEvents against scheduled Tasks. If CalendarEvent overlaps Task's scheduledStart/scheduledEnd and task is not locked, marks task for rescheduling. Verify: Create scheduled task 10:00-11:00, sync calendar event 10:30-11:30, task marked for rescheduling.
  • Automatic rescheduling on conflict: ReschedulingService finds next available slot for displaced tasks (respecting working hours, context, priority), updates scheduledStart/scheduledEnd, creates ReschedulingEvent record with reason, original/new times. Verify: Trigger conflict, rescheduling runs, task moves to 11:30-12:30, ReschedulingEvent created.
  • WebSocket gateway setup: NestJS WebSocket gateway (@WebSocketGateway()) with JWT authentication guard. Clients connect to ws://localhost:3000. On connection, server stores userId-to-socketId mapping. Verify: Frontend connects via socket.io-client, connection established, getaddrinfo command shows WebSocket connection in logs.
  • Real-time rescheduling notifications: When ReschedulingService reschedules task, emits WebSocket event task:rescheduled with payload {taskId, originalStart, newStart, reason} to user's socket. Frontend socket.io client listens, shows toast notification, refetches calendar via React Query invalidation. Verify: Open browser with WebSocket devtools, trigger reschedule via API, see WebSocket message received, toast appears.
  • Notification persistence: All rescheduling events create Notification entity with type=RESCHEDULING, message, relatedEntityId=taskId. GET /api/v1/notifications returns unread notifications. PATCH /api/v1/notifications/:id/read marks as read. Frontend shows notification bell icon with count. Verify: Reschedule task, GET notifications returns 1 unread, click mark read, count decreases.
  • Email notification for rescheduling: If user's notificationPreferences.email=true, NotificationService sends email via SMTP (nodemailer) with rescheduling details. Email template includes task title, old/new times, reason. Verify: Mock SMTP server (MailHog), trigger reschedule, check MailHog for email.
  • Webhook notification: If notificationPreferences.webhook=true and webhookUrl configured, NotificationService posts JSON payload to webhook URL on rescheduling. Verify: Mock webhook endpoint (webhook.site), trigger reschedule, see POST request received with correct payload.
  • Weekly Review auto-scheduling: On user creation or preferences update, WeeklyReviewService creates recurring Task with status=WEEKLY_REVIEW, scheduledStart at user-configured day/time (e.g., Friday 4 PM), duration 60 min, isLocked=true. Cron job checks weekly, ensures review task exists for upcoming week. Verify: Set weekly review time in preferences, trigger cron, GET tasks shows recurring review task at configured time.
  • Weekly Review interface: GET /api/v1/weekly-review endpoint returns aggregated data: active projects count, projects without next actions (flagged), total Next Actions grouped by project, unprocessed inbox count, Waiting For items with overdue follow-ups, Someday/Maybe count. Frontend /weekly-review page displays checklist UI. Verify: Create 2 projects (1 with tasks, 1 without), 5 inbox items, GET weekly-review returns {"activeProjects":2,"projectsWithoutNextActions":1,"inboxCount":5,...}.
  • Weekly Review completion: POST /api/v1/weekly-review/complete updates User.lastWeeklyReview timestamp. Verify: Complete review, GET user preferences shows lastWeeklyReview updated.
  • Rate limiting on external API calls: Bull queue configured for calendar, ConnectWise, email sync jobs with concurrency=1, rate limiter (1 request per 2 seconds to respect API limits). Verify: Queue 10 sync jobs, monitor logs, jobs execute sequentially with delay.
  • Error handling for external APIs: Sync services wrap API calls in try-catch, log errors, create system Notification for user if sync fails (e.g., "ConnectWise sync failed: invalid credentials"). Failed jobs retry 3 times with exponential backoff (Bull retry strategy). Verify: Mock ConnectWise API to return 401, trigger sync, see error notification created, job retries logged.
  • Encryption for credentials: Database credentials (IMAP passwords, API keys) encrypted with AES-256 using secret from environment variable before persisting. Decrypted on retrieval. Use crypto module or @nestjs/config with encryption utility. Verify: Inspect database, credentials columns show encrypted strings; GET connection via API returns functional connection (decrypt succeeds).

Phase 3 Verification Command:

TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -d '{"email":"verify@test.com","password":"Test123!"}' \
  -H "Content-Type: application/json" | jq -r .token) && \
curl -f -X POST -H "Authorization: Bearer $TOKEN" \
  http://localhost:3000/api/v1/connections/calendar \
  -d '{"provider":"CALDAV","calendarUrl":"http://mock","credentials":{"username":"test","password":"pass"}}' \
  -H "Content-Type: application/json" && \
curl -f -X GET -H "Authorization: Bearer $TOKEN" \
  http://localhost:3000/api/v1/weekly-review | jq .inboxCount | grep -E '^[0-9]+$' && \
echo "✓ Phase 3 Complete"

Phase 4: Polish

Goal: Comprehensive documentation, deployment hardening, performance optimization, accessibility audit, error boundary improvements, and final testing. Prepare for production self-hosted deployment.

Completion Criteria

  • README.md complete: Root README includes: project overview, architecture diagram (Mermaid or ASCII), prerequisites (Docker, Docker Compose, Node.js for dev), quick-start instructions (clone, cp .env.example .env, docker compose up), environment variable documentation (all required vars listed with descriptions), default ports, access URLs. Verify: Fresh clone on new machine, follow README, app starts successfully.
  • Docker Compose production configuration: docker-compose.prod.yml with: PostgreSQL persistent volume, Redis persistent volume, backend health checks, restart policies (unless-stopped), resource limits (CPU/memory), nginx serving frontend static files with gzip, security headers (Helmet). Verify: docker compose -f docker-compose.prod.yml up -d, all services start with resource constraints visible in docker stats.
  • Environment variable validation: Backend startup validates required env vars (DATABASE_URL, REDIS_URL, JWT_SECRET, ENCRYPTION_KEY) using @nestjs/config with Joi schema. Missing vars log error and exit process. Verify: Remove DATABASE_URL from .env, start backend, see error "Missing required env var: DATABASE_URL" and exit code 1.
  • Database migrations documentation: packages/backend/README.md documents migration workflow: pnpm migration:generate -n MigrationName, pnpm migration:run, pnpm migration:revert. Includes note on production migrations (run before deploying new backend version). Verify: Generate dummy migration, run it, verify in DB, re