Evolve Tasks
A single-tenant task-management and lightweight CRM platform built to give a small real-estate team ClickUp-level functionality on owned infrastructure instead of a per-seat…
- 763
- vitest tests + 6 Playwright e2e tests
- 590
- commits in git history
- 75
- Drizzle migrations
- 375
- test files in src/
- Next.js 16 (App Router, RSC, Tur…
- React 19.2
- Tailwind CSS v4
- Cloudflare Workers via OpenNext
- Cloudflare D1 (SQLite)
- Cloudflare R2
- Anthropic Claude API
- Drizzle ORM
- vitest + Playwright
- arctic (OAuth)
Evolve Tasks holds live data, so this shows the verified technology stack by layer rather than a screenshot. Hosts, ports and topology are deliberately absent.
Problem
The team was running project and client work through ClickUp, a per-seat SaaS tool not built around a real-estate brokerage's actual workflow: transactions with Florida-specific compliance deadlines (inspection, financing, title, appraisal, walkthrough), commission splits, BBA/TCPA consent tracking, and wire-fraud verification steps. Fitting that domain into a generic project-management tool meant workarounds everywhere, plus a recurring per-seat cost with no path to owning the data model or the infrastructure it ran on. None of ClickUp's native fields modeled a Florida real-estate transaction as a first-class object, so deadlines, disclosures, and commission splits had to live in custom fields and naming conventions bolted onto a generic task, which meant every new compliance requirement was another manual convention for the whole six-person team to remember and apply consistently rather than a rule the software itself enforced. The team also had no AI assistant wired to its own data inside ClickUp — any AI help meant copying context out to a separate tool by hand — and no way to bound what an AI feature would cost per user per day, since that lever didn't exist in the vendor's product at all.
What was built
Evolve Tasks is a team task-and-CRM platform purpose-built for one real-estate brokerage's actual workflow, deployed on Cloudflare's edge network instead of a third-party SaaS vendor. It gives the team everything ClickUp did — list, board, table, calendar, and Gantt views, sprints, goals, time tracking, a command palette, real-time-feeling chat with deal-specific rooms, and a docs surface where selecting text can spin off a linked task — plus things ClickUp didn't have: client, listing, and transaction records with Florida disclosure and Buyer Broker Agreement tracking, commission-split ledgers, wire-fraud verification callouts on large transactions, and an AI assistant (Brain) that drafts and answers questions against the team's own data under a hard daily spend cap per user. Sign-in is Google OAuth gated to the team's own email domain; admins can view-as any user, deactivate accounts, and force a global sign-out in one action.
Technical approach
The app is a Next.js 16 App Router application deployed as a Cloudflare Worker via the OpenNext adapter (Cloudflare deprecated the older next-on-pages path for SSR apps), backed by a single Cloudflare D1 SQLite database accessed through Drizzle ORM and an R2 bucket for attachments — deliberately single-tenant, with no tenant_id column anywhere, since it's one team's own workspace. Auth is app-level Google OAuth via the arctic library rather than NextAuth or Cloudflare Access (Access's default email-OTP UX was rejected), issuing an HS256 JWT session cookie whose domain claim is checked both in edge middleware and again at page-render time; a tokenVersion claim lets any single user's sessions be invalidated instantly without touching anyone else's. Content-Security-Policy uses per-request nonces on script-src with no unsafe-inline and no strict-dynamic, plumbed through a custom header Next 16 stamps onto every inline script it emits — hardening that was done in two passes, since CSP3 browsers additionally block unsigned inline <style> tags and that had to be nonced separately. The most consequential engineering thread across the project's history is a series of formal authorization sweeps: three separate audits found and closed 19, then 27, then 61 server-action authorization gaps (IDOR-class — a user able to read or act on another user's or another space's record by guessing an ID), on top of an initial security-baseline wave covering CSP/HSTS headers, role gates on destructive actions, and a race-condition fix across 14 call sites for drag-and-drop position updates. Real-time chat was originally built on a Durable Object but that implementation was reverted after a production regression and replaced with 1.5-second HTTP polling — a deliberate downgrade in mechanism kept for reliability over architectural purity. A dedicated compliance layer implements CCPA right-to-erasure (redact-in-place while preserving FREC hold requirements) and DSAR export, SHA-256 integrity verification on file attachments with magic-byte MIME sniffing, and a wire-fraud safeguard that surfaces verified phone numbers with click-to-call on large active transactions rather than trusting whatever contact info is on file.
Creative approach
Craft
The visual system carries forward Monarch design tokens through a Tailwind v4 @theme inline bridge, paired with Plus Jakarta Sans for UI text and Instrument Serif for brand moments — a deliberate two-typeface split between a functional, dense operational surface (lists, boards, tables) and a handful of brand-forward touchpoints. Inbox notifications use adjacent-row coalescing (twelve status changes by one person on one deal collapse into a single row with a ×12 badge) rather than a flooded activity feed, and a global EmptyState component and ConfirmDialog/Toast provider pair replaced native window.confirm/window.alert calls across the app so destructive actions and empty views read consistently everywhere instead of varying screen to screen.
Reframe
The core product insight was refusing to treat 'task management' and 'real-estate CRM' as two different tools bolted together: transactions, clients, and listings live as first-class records in the same schema as tasks and can be linked to them directly, so a compliance deadline or a commission split is not a separate system to reconcile against the task board — it is the task board. The build sequence made this explicit: an initial ten-session roadmap shipped the generic project-management surface first, then a much longer series of 'hardening waves' (documented as role-based gap audits run against the live tool) progressively grafted on the real-estate-specific domain — FL statute disclosure tracking, wire-fraud verification, 1099-NEC export, BBA lifecycle — onto the same task/records substrate rather than starting a parallel CRM module.
Process and what failed
The project's own README documents its failures candidly rather than smoothing them over: a real-time chat feature built on a Cloudflare Durable Object was shipped, caused a production regression, and was reverted wholesale to plain HTTP polling — a deliberate step down in sophistication kept because it was the version that didn't break. A search LIKE query using Drizzle's like() helper was silently returning zero results for any query containing a literal % or _ character because Drizzle doesn't emit an ESCAPE clause by default; caught and fixed in a dedicated round of the post-launch test sprint. A recurring monthly-task scheduler was overflowing dates incorrectly (Jan 31 plus one month landing on March 3 instead of clamping to Feb 28), also caught by the same test-hardening effort. The project ran three distinct, separately-numbered authorization audits over its life (19, then 27, then 61 confirmed IDOR/authz findings) rather than treating access control as solved after the first pass — each subsequent audit found a new category of gap the previous one missed (personal-space leaks in the whiteboard/mind-map feature, OAuth open-redirect, impersonation-actor logging).
Outcome
Evolve Tasks is live in production at the team's custom domain, gating sign-in to the brokerage's own Google Workspace email domain, and has replaced ClickUp as the team's day-to-day task and CRM system. The test suite (763 unit tests plus Playwright end-to-end and accessibility checks, the latter run through axe-core) and CI-gated deploy pipeline run on every push to main, with a weekly automated D1 backup-restore drill that verifies the backup is actually restorable rather than just confirming a backup job ran.
Operational hardening kept pace with feature work rather than trailing it. An insider-threat wave added admin-gated bulk-export routes, a token-version claim wired to a "Sign out everywhere" control that cuts the window a stolen or lost phone stays signed in from up to seven days down to a single request, sign-in audit logging with Cloudflare-supplied IP, country, and user-agent, and cron-driven anomaly detection for mass-download or mass-export behavior. A maintenance cron sweeps trash garbage collection, notification garbage collection, and per-job error isolation on a schedule rather than relying on manual cleanup, with ntfy failure alerts and a heartbeat ping so a silently-failed cron run gets surfaced instead of discovered later. The Workers bundle is tracked against Cloudflare's free-tier size ceiling with an early-warning threshold at 85% utilization, so a dependency addition that would tip the deploy over the cliff gets caught before it ships rather than after wrangler rejects the upload.