30 KiB
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
-
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
-
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
-
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)
-
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
-
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. Rootpackage.jsonhas workspace scripts:pnpm dev,pnpm build,pnpm test. Verify:pnpm install && pnpm -r listshows all three workspaces. - Backend NestJS app scaffolded with modules:
AuthModule,UsersModule,TasksModule,ProjectsModule,InboxModule. Verify:pnpm --filter backend buildsucceeds,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 devstarts dev server on port 5173, homepage renders. - PostgreSQL database schema created via TypeORM migrations:
User,InboxItem,Task,Project,CalendarConnection,CalendarEvent,ConnectWiseConnection,EmailConnection,ReferenceMaterial,ReschedulingEvent,Notificationentities with all fields and relationships from spec. Verify:pnpm --filter backend migration:runsucceeds,psql -d autoscheduler -c "\dt"lists 11 tables. - Docker Compose stack configured:
backend,postgres,redis,frontendservices. Backend exposes port 3000, frontend port 80, PostgreSQL port 5432 (internal only). Environment variables loaded from.envfile. Verify:docker compose up -d && docker compose psshows all services healthy,curl http://localhost:3000/healthreturns{"status":"ok"}. - Authentication implemented: JWT-based auth with Passport.js.
/api/v1/auth/register,/api/v1/auth/login,/api/v1/auth/refreshendpoints 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, subsequentcurl -H "Authorization: Bearer <token>" http://localhost:3000/api/v1/users/mereturns 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/loginif missing. Axios interceptor addsAuthorizationheader. 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/nonexistentreturns 404 JSON,curl -H "Authorization: Bearer invalid" http://localhost:3000/api/v1/users/mereturns 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-typespackage exports DTOs (CreateTaskDto, TaskResponseDto, etc.) used by both backend validators and frontend Zod schemas. Verify: Change a field type inshared-types, runpnpm build, see TypeScript errors in both frontend and backend if mismatched. - Health checks:
/healthendpoint returns database connection status, Redis connection status. Docker Compose health checks configured for backend (GET /health every 30s). Verify:curl http://localhost:3000/healthreturns{"status":"ok","database":"connected","redis":"connected"},docker compose psshows backend ashealthy.
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/inboxcreates 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/inboxreturns all unprocessed items for authenticated user.POST /api/v1/inbox/:id/processaccepts action payload (e.g.,{"action":"task","context":"@desk","domain":"work","title":"Do thing"}) and converts inbox item to Task, Project, or marks as Trash, settingprocessed=true. Verify: Create inbox item, process to task, GET inbox returns empty array,GET /api/v1/tasksincludes 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 viaPATCH /api/v1/tasks/:idwithprojectId,GET /api/v1/projects/:id/tasksreturns associated tasks. - Reference material attachments:
POST /api/v1/projects/:id/referenceaccepts 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/:idincludes references array. Verify: Upload PDF to project, GET project returns reference with file path, file accessible at/uploads/:filenameroute. - Waiting For follow-up dates: Tasks with status=WAITING_FOR accept
followUpDatetimestamp. Scheduled job (cron) runs daily, identifies Waiting For items wherefollowUpDate <= 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 inGET /api/v1/notifications. - Tickler activation: Tasks with status=TICKLER have
ticklerDate. Daily cron job checks forticklerDate <= 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/preferencesreturns user's workingHours (JSON:{"monday":{"start":"09:00","end":"17:00"},...}), timezone, notificationPreferences (JSON:{"email":true,"webhook":false,"webhookUrl":""}).PATCH /api/v1/user/preferencesupdates 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/regeneratetriggers scheduling: fetches all NEXT_ACTION tasks withoutscheduledStart, assignsscheduledStart/scheduledEndwithin available slots, respectingestimatedDuration(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/scheduledEndas 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
eventDropcallback fires on drag, PATCH/api/v1/tasks/:idwith newscheduledStart/scheduledEnd, setsisLocked=trueto prevent auto-rescheduling.eventResizecallback handles duration changes. Verify: Drag task in calendar, reload page, task remains at new time,isLocked=truein DB. - Task lock/unlock:
POST /api/v1/tasks/:id/locksetsisLocked=true,POST /api/v1/tasks/:id/unlocksetsisLocked=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-actionsfetchesGET /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
/projectsfetchesGET /api/v1/projects, renders cards with name, domain, active task count. Click card navigates to/projects/:iddetail 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-forfetchesGET /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-maybefetchesGET /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/calendarcreates CalendarConnection with provider=CALDAV, calendarUrl, credentials (username/password encrypted at rest).GET /api/v1/connections/calendarlists user's connections.DELETE /api/v1/connections/calendar/:idremoves connection. Verify: Create CalDAV connection with Nextcloud URL, GET returns connection with masked credentials. - CalDAV sync job:
CalendarSyncServiceusingnode-caldavlibrary 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/calendarwith provider=MICROSOFT_GRAPH initiates OAuth flow, stores refresh token.CalendarSyncServiceuses@microsoft/microsoft-graph-clientto 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/calendarwith provider=GOOGLE initiates OAuth flow (or accepts service account JSON).CalendarSyncServiceuses Google Calendar API via axios/googleapis. Verify: Mock Google API responses, trigger sync, events persisted with externalId. - ConnectWise Manage connection:
POST /api/v1/connections/connectwiseaccepts companyId, publicKey, privateKey, apiUrl, memberId.ConnectWiseServiceusing axios queries/service/tickets,/project/tickets,/project/projectswith conditionsowner/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/ticketscount=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
connectwisePriorityandconnectwiseSLAfields (strings). Displayed in UI for reference but do not affect scheduling priority (user manually setspriorityinteger). Verify: Process ConnectWise inbox item to task, task hasconnectwisePriority="High",priority=null, user updatespriority=1, scheduling uses user priority. - IMAP email connection:
POST /api/v1/connections/emailwith provider=IMAP accepts imapHost, imapPort, credentials, inboxFolder.EmailSyncServiceusingimaplibrary 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/emailwith 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,
ConflictDetectionServicecompares new CalendarEvents against scheduled Tasks. If CalendarEvent overlaps Task'sscheduledStart/scheduledEndand 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:
ReschedulingServicefinds next available slot for displaced tasks (respecting working hours, context, priority), updatesscheduledStart/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 tows://localhost:3000. On connection, server stores userId-to-socketId mapping. Verify: Frontend connects via socket.io-client, connection established,getaddrinfocommand shows WebSocket connection in logs. - Real-time rescheduling notifications: When ReschedulingService reschedules task, emits WebSocket event
task:rescheduledwith 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/notificationsreturns unread notifications.PATCH /api/v1/notifications/:id/readmarks 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,NotificationServicesends 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=trueandwebhookUrlconfigured,NotificationServiceposts 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,
WeeklyReviewServicecreates recurring Task with status=WEEKLY_REVIEW,scheduledStartat 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-reviewendpoint 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-reviewpage 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/completeupdates 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
cryptomodule or@nestjs/configwith 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.ymlwith: 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 indocker stats. - Environment variable validation: Backend startup validates required env vars (DATABASE_URL, REDIS_URL, JWT_SECRET, ENCRYPTION_KEY) using
@nestjs/configwith 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.mddocuments 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